diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d3e17c050..fc9e7b1bd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Braintree iOS SDK Release Notes +## unreleased +* BraintreeApplePay + * Add `BTApplePayCardNonce.isDeviceToken` for MPAN identification + ## 5.26.0 (2024-05-07) * Updated expiring pinned vendor SSL certificates diff --git a/Demo/Application/Features/Apple Pay - PassKit/BraintreeDemoApplePayPassKitViewController.m b/Demo/Application/Features/Apple Pay - PassKit/BraintreeDemoApplePayPassKitViewController.m index dd4abf8f1d..578c2b6c58 100644 --- a/Demo/Application/Features/Apple Pay - PassKit/BraintreeDemoApplePayPassKitViewController.m +++ b/Demo/Application/Features/Apple Pay - PassKit/BraintreeDemoApplePayPassKitViewController.m @@ -82,12 +82,44 @@ - (void)tappedApplePayButton { PKPaymentAuthorizationViewController *viewController = [[PKPaymentAuthorizationViewController alloc] initWithPaymentRequest:paymentRequest]; viewController.delegate = self; - + + if (@available(iOS 16.0, *)) { + PKRecurringPaymentSummaryItem *recurringItem = [self createRecurringPaymentSummaryItem]; + NSURL *managementURL = [NSURL URLWithString:@"https://example.com/manage"]; + + PKRecurringPaymentRequest *recurringPaymentRequest = [ + [PKRecurringPaymentRequest alloc] initWithPaymentDescription:@"Payment description." + regularBilling:recurringItem + managementURL:managementURL + ]; + } + self.progressBlock(@"Presenting Apple Pay Sheet"); [self presentViewController:viewController animated:YES completion:nil]; }]; } +- (PKRecurringPaymentSummaryItem *)createRecurringPaymentSummaryItem API_AVAILABLE(ios(15.0)){ + NSDecimalNumber *amount = [NSDecimalNumber decimalNumberWithString:@"10.99"]; + NSDate *startDate = [[NSCalendar currentCalendar] dateByAddingUnit:NSCalendarUnitMonth + value:1 + toDate:[NSDate date] + options:0]; + NSDate *endDate = [[NSCalendar currentCalendar] dateByAddingUnit:NSCalendarUnitMonth + value:12 + toDate:startDate + options:0]; + + PKRecurringPaymentSummaryItem *recurringItem = [[PKRecurringPaymentSummaryItem alloc] init]; + recurringItem.label = @"Subscription Payment"; + recurringItem.amount = amount; + recurringItem.startDate = startDate; + recurringItem.intervalUnit = NSCalendarUnitMonth; // Monthly billing cycle + recurringItem.intervalCount = 1; // Every 1 month + recurringItem.endDate = endDate; // Optional end date + + return recurringItem; +} #pragma mark PKPaymentAuthorizationViewControllerDelegate diff --git a/Sources/BraintreeApplePay/BTApplePayCardNonce.m b/Sources/BraintreeApplePay/BTApplePayCardNonce.m index 74670e1435..e453ee0280 100644 --- a/Sources/BraintreeApplePay/BTApplePayCardNonce.m +++ b/Sources/BraintreeApplePay/BTApplePayCardNonce.m @@ -11,6 +11,7 @@ - (instancetype)initWithJSON:(BTJSON *)json { self = [super initWithNonce:[json[@"nonce"] asString] type:cardType isDefault:[json[@"default"] isTrue]]; if (self) { + _isDeviceToken = [json[@"details"][@"isDeviceToken"] isTrue]; _binData = [[BTBinData alloc] initWithJSON:json[@"binData"]]; } return self; diff --git a/Sources/BraintreeApplePay/Public/BraintreeApplePay/BTApplePayCardNonce.h b/Sources/BraintreeApplePay/Public/BraintreeApplePay/BTApplePayCardNonce.h index 2c7c1b344e..f062f2b384 100644 --- a/Sources/BraintreeApplePay/Public/BraintreeApplePay/BTApplePayCardNonce.h +++ b/Sources/BraintreeApplePay/Public/BraintreeApplePay/BTApplePayCardNonce.h @@ -16,6 +16,11 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic, readonly, strong) BTBinData *binData; +/** + This Boolean (available on iOS 16+) indicates whether this tokenized card is a device-specific account number (DPAN) or merchant/cloud token (MPAN). If `isDeviceToken` is `false`, then token type is MPAN. + */ +@property (nonatomic, assign) BOOL isDeviceToken; + /** Used to initialize a `BTApplePayCardNonce` with parameters. */ diff --git a/UnitTests/BraintreeApplePayTests/BTApplePayCardNonce_Tests.swift b/UnitTests/BraintreeApplePayTests/BTApplePayCardNonce_Tests.swift index f6bee4ef17..2de3226ddd 100644 --- a/UnitTests/BraintreeApplePayTests/BTApplePayCardNonce_Tests.swift +++ b/UnitTests/BraintreeApplePayTests/BTApplePayCardNonce_Tests.swift @@ -33,4 +33,42 @@ class BTApplePayCardNonce_Tests: XCTestCase { XCTAssertEqual(applePayNonce?.type, "ApplePayCard") } + func testInitWithJSON_whenApplePayTokenIsMPAN() { + 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_whenApplePayTokenIsDPAN() { + 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) + } + }