Skip to content
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
cfded06
Adds new result type for `collectPaymentMethod` to sometimes return K…
mliberatore Mar 19, 2026
bf2d135
Adds helper extensions for payment details → KycInfo
mliberatore Mar 19, 2026
83dc992
Updates the CryptoOnrampCoordinator API and adapts the example app to…
mliberatore Mar 19, 2026
68b4522
Updates README about the API behavior addition
mliberatore Mar 19, 2026
30d5e8b
Also updates the ApplePay code path in PaymentView to maintain existi…
mliberatore Mar 19, 2026
30ee2ee
Merge branch 'master' into mliberatore/crypto-onramp-apple-pay-kyc
mliberatore Mar 19, 2026
43b06aa
Adds test code for testing the Apple Pay L0 KYC flow
mliberatore Mar 19, 2026
e0a517e
Revert "Adds test code for testing the Apple Pay L0 KYC flow"
mliberatore Mar 19, 2026
3228106
Merge branch 'master' into mliberatore/crypto-onramp-apple-pay-kyc
mliberatore Mar 19, 2026
3a61861
Adds docs to address extension
mliberatore Mar 19, 2026
caaca88
Adds unit test for billing contact → KycInfo conversion and Address.i…
mliberatore Mar 19, 2026
21fab80
Merge branch 'master' into mliberatore/crypto-onramp-apple-pay-kyc
Twigz Mar 25, 2026
6cb1cee
PR review cleanup, additional tests
Twigz Mar 31, 2026
592046a
Merge branch 'master' into mliberatore/crypto-onramp-apple-pay-kyc
Twigz Mar 31, 2026
90c1915
Fix linting
Twigz Mar 31, 2026
cbf48c3
Merge branch 'master' into mliberatore/crypto-onramp-apple-pay-kyc
Twigz Apr 6, 2026
fa2966c
Allow for proper cancellation of payment collection
Twigz Apr 7, 2026
c59f3d3
Merge branch 'master' into twigz/proper-payment-collection-cancellation
Twigz Apr 9, 2026
922899d
Merge branch 'master' into twigz/proper-payment-collection-cancellation
Twigz Apr 9, 2026
b5cf8e5
Previous selectedPaymentSource for ApplePay
Twigz Apr 9, 2026
f718c6f
Update CryptoOnrampCoordinator.swift
Twigz Apr 9, 2026
6a75425
Merge branch 'master' into twigz/proper-payment-collection-cancellation
mliberatore Apr 13, 2026
e0d0283
Renames `applePayPreviousPaymentSource` and adds clear docs
mliberatore Apr 13, 2026
a4615f7
Merge branch 'master' into twigz/proper-payment-collection-cancellation
mliberatore Apr 13, 2026
9845044
Merge branch 'master' into twigz/proper-payment-collection-cancellation
Twigz Apr 20, 2026
87ed9c1
Merge branch 'master' into twigz/proper-payment-collection-cancellation
Twigz Apr 21, 2026
5834398
Merge branch 'master' into twigz/proper-payment-collection-cancellation
Twigz Apr 21, 2026
73a636f
Applies Jean’s suggestion for reversing `paymentSourceBeforeApplePay`…
mliberatore Apr 22, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,14 @@ public final class CryptoOnrampCoordinator: NSObject, CryptoOnrampCoordinatorPro
private let appearance: LinkAppearance
private let analyticsClient: CryptoOnrampAnalyticsClient
private var applePayCompletionContinuation: CheckedContinuation<ApplePayPaymentStatus, Swift.Error>?

/// Temporary snapshot of the selection that existed before starting an Apple Pay collection flow.
///
/// We set this immediately before presenting Apple Pay so we can restore `selectedPaymentSource`
/// if the Apple Pay flow is canceled or fails after `didCreatePaymentMethod` has already updated
/// `selectedPaymentSource`. This value should only represent pre-existing selection state for the
/// active Apple Pay attempt, and must be cleared once that attempt completes, fails, or the user logs out.
private var paymentSourceBeforeApplePay: SelectedPaymentSource?
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have we considered flipping the direction here? Instead of saving what we had before and restoring on cancel, we could use a pendingApplePayPaymentSource that only gets promoted to selectedPaymentSource in didCompleteWith(.success) - leaving it untouched on cancel/error, with nothing to restore. It'd also make cleanup symmetric: just pendingApplePayPaymentSource = nil at the end of didCompleteWith in all cases.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, hadn't thought about it that way. It reduces the overall management of that state. I‘ve implemented the suggestion in 73a636f.

private var selectedPaymentSource: SelectedPaymentSource?
private let cryptoCustomerState: CryptoCustomerState

Expand Down Expand Up @@ -478,7 +486,6 @@ public final class CryptoOnrampCoordinator: NSObject, CryptoOnrampCoordinatorPro
supportedPaymentMethodTypes: supportedPaymentMethodTypes,
collectName: type.requiresNameCollection
) else {
selectedPaymentSource = nil
return .canceled
}

Expand All @@ -493,6 +500,7 @@ public final class CryptoOnrampCoordinator: NSObject, CryptoOnrampCoordinatorPro
return .completed(displayData: preview, kycInfo: nil)
case .applePay(let paymentRequest):
// This presents Apple Pay and fills the selected payment source in the delegate.
paymentSourceBeforeApplePay = selectedPaymentSource
do {
let status = try await presentApplePay(using: paymentRequest, from: viewController)
switch status {
Expand Down Expand Up @@ -520,14 +528,15 @@ public final class CryptoOnrampCoordinator: NSObject, CryptoOnrampCoordinatorPro
sublabel: sublabel
)

paymentSourceBeforeApplePay = nil
analyticsClient.log(.collectPaymentMethodCompleted(paymentMethodType: type.analyticsValue))

return .completed(displayData: paymentMethodPreview, kycInfo: kycInfo)
case .canceled:
selectedPaymentSource = nil
return .canceled
}
} catch {
paymentSourceBeforeApplePay = nil
analyticsClient.log(.errorOccurred(during: .collectPaymentMethod, errorMessage: error.localizedDescription))
throw error
}
Expand Down Expand Up @@ -629,6 +638,8 @@ public final class CryptoOnrampCoordinator: NSObject, CryptoOnrampCoordinatorPro

public func logOut() async throws {
do {
paymentSourceBeforeApplePay = nil
selectedPaymentSource = nil
try await linkController.logOut()
analyticsClient.log(.userLoggedOut)
} catch {
Expand Down Expand Up @@ -657,16 +668,17 @@ extension CryptoOnrampCoordinator: ApplePayContextDelegate {
case .success:
applePayCompletionContinuation?.resume(returning: .success)
case .userCancellation:
selectedPaymentSource = nil
selectedPaymentSource = paymentSourceBeforeApplePay
applePayCompletionContinuation?.resume(returning: .canceled)
case .error:
selectedPaymentSource = nil
selectedPaymentSource = paymentSourceBeforeApplePay
applePayCompletionContinuation?.resume(throwing: error ?? ApplePayPaymentStatus.Error.applePayFallbackError)
@unknown default:
selectedPaymentSource = nil
selectedPaymentSource = paymentSourceBeforeApplePay
applePayCompletionContinuation?.resume(throwing: error ?? ApplePayPaymentStatus.Error.applePayFallbackError)
}

paymentSourceBeforeApplePay = nil
applePayCompletionContinuation = nil
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ import UIKit
with email: String?,
supportedPaymentMethodTypes: [LinkPaymentMethodType] = LinkPaymentMethodType.allCases,
collectName: Bool = false,
completion: @escaping () -> Void
completion: @escaping (_ didSelectPaymentMethod: Bool) -> Void
) {
var configuration = self.configuration
configuration.defaultBillingDetails.email = email
Expand All @@ -395,12 +395,12 @@ import UIKit
if shouldClearSelection {
self?.internalPaymentOption = nil
}
completion()
completion(false)
return
}

self?.internalPaymentOption = .link(option: confirmOption)
completion()
completion(true)
}
}

Expand Down Expand Up @@ -1037,9 +1037,9 @@ extension LinkController: LinkFullConsentViewControllerDelegate {
with: email,
supportedPaymentMethodTypes: supportedPaymentMethodTypes,
collectName: collectName
) { [weak self] in
) { [weak self] didSelectPaymentMethod in
guard let self else { return }
continuation.resume(returning: self.paymentMethodPreview)
continuation.resume(returning: didSelectPaymentMethod ? self.paymentMethodPreview : nil)
}
}
}
Expand Down
Loading