Skip to content

Commit

Permalink
Merge mpan feature branch into main (#1521)
Browse files Browse the repository at this point in the history
* Exposed `isDeviceToken`

* Added unit tests

* Changed default to `true`

* Updated demo app

* Expose `isDeviceToken` on BTApplePayCardNonce (#1516)

* Addressed PR comments
  • Loading branch information
stechiu authored Feb 13, 2025
1 parent 0c4d3af commit 05f1b03
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 2 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Braintree iOS SDK Release Notes

## unreleased
* BraintreeApplePay
* Add `BTApplePayCardNonce.isDeviceToken` for MPAN identification

## 6.28.0 (2025-02-05)
* BraintreeVenmo
* Allow universal links to be set without a return URL scheme (fixes #1505)
Expand Down
22 changes: 20 additions & 2 deletions Demo/Application/Features/ApplePayViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import PassKit
class ApplePayViewController: PaymentButtonBaseViewController {

lazy var applePayClient = BTApplePayClient(apiClient: apiClient)
// swiftlint:disable:next force_unwrapping
let managementURL = URL(string: "https://www.merchant.com/update-payment")!

override func viewDidLoad() {
super.viewDidLoad()
Expand Down Expand Up @@ -36,15 +38,31 @@ class ApplePayViewController: PaymentButtonBaseViewController {
}

let paymentRequest = self.constructPaymentRequest(with: request)
// swiftlint:disable:next force_unwrapping
let paymentAuthorizationViewController = PKPaymentAuthorizationViewController(paymentRequest: paymentRequest)!
guard let paymentAuthorizationViewController = PKPaymentAuthorizationViewController(paymentRequest: paymentRequest) else {
self.progressBlock("Could not create PKPaymentAuthorizationViewController")
return
}
paymentAuthorizationViewController.delegate = self

if #available(iOS 16.0, *) {
paymentRequest.recurringPaymentRequest = self.recurringPaymentRequest()
}

self.progressBlock("Presenting Apple Pay Sheet")
self.present(paymentAuthorizationViewController, animated: true)
}
}

@available(iOS 16.0, *)
private func recurringPaymentRequest() -> PKRecurringPaymentRequest {
let recurringPaymentRequest = PKRecurringPaymentRequest(
paymentDescription: "Payment description.",
regularBilling: PKRecurringPaymentSummaryItem(label: "Payment label", amount: 10.99),
managementURL: managementURL
)
return recurringPaymentRequest
}

private func constructPaymentRequest(with paymentRequest: PKPaymentRequest) -> PKPaymentRequest {
paymentRequest.requiredBillingContactFields = [PKContactField.name]

Expand Down
6 changes: 6 additions & 0 deletions Sources/BraintreeApplePay/BTApplePayCardNonce.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,18 @@ import BraintreeCore
/// The BIN data for the card number associated with this nonce.
public let binData: BTBinData

/// This Boolean indicates whether this tokenized card is a device-specific account number (DPAN) or merchant/cloud token (MPAN). Available on iOS 16+.
/// If `isDeviceToken` is `false`, then token type is MPAN
public var isDeviceToken: Bool

init?(json: BTJSON) {
guard let nonce = json["nonce"].asString() else { return nil }

let cardType = json["details"]["cardType"].asString() ?? "ApplePayCard"
let isDefault = json["default"].isTrue

self.isDeviceToken = json["details"]["isDeviceToken"].asBool() ?? true

binData = BTBinData(json: json["binData"])
super.init(nonce: nonce, type: cardType, isDefault: isDefault)
}
Expand Down
38 changes: 38 additions & 0 deletions UnitTests/BraintreeApplePayTests/BTApplePayCardNonce_Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,44 @@ class BTApplePayCardNonce_Tests: XCTestCase {
XCTAssertEqual(applePayNonce?.type, "fake-card-type")
}

func testInitWithJSON_whenApplePayTokenIsMPAN() {
let applePayCard = BTJSON(
value: [
"consumed": false,
"binData": [
"commercial": "yes"
],
"details": [
"cardType": "fake-card-type",
"isDeviceToken": false
],
"nonce": "a-nonce"
] as [String: Any]
)

let applePayNonce = BTApplePayCardNonce(json: applePayCard)
XCTAssertEqual(applePayNonce?.isDeviceToken, false)
}

func testInitWithJSON_whenApplePayTokenIsDPAN() {
let applePayCard = BTJSON(
value: [
"consumed": false,
"binData": [
"commercial": "yes"
],
"details": [
"cardType": "fake-card-type",
"isDeviceToken": true
],
"nonce": "a-nonce"
] as [String: Any]
)

let applePayNonce = BTApplePayCardNonce(json: applePayCard)
XCTAssertEqual(applePayNonce?.isDeviceToken, true)
}

func testInitWithJSON_setsDefaultProperties() {
let applePayCard = BTJSON(
value: [
Expand Down

0 comments on commit 05f1b03

Please sign in to comment.