Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
32 changes: 32 additions & 0 deletions ios/ApplePayUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,32 @@ class ApplePayUtils {
return request
}

@available(iOS 16.4, *)
internal class func buildDeferredPaymentRequest(params: NSDictionary) throws -> PKDeferredPaymentRequest {
guard let description = params["paymentDescription"] as? String else {
throw ApplePayUtilsError.missingParameter(nil, "paymentDescription")
}
guard let urlString = params["managementUrl"] as? String else {
throw ApplePayUtilsError.missingParameter(nil, "managementUrl")
}
guard let url = URL(string: urlString) else {
throw ApplePayUtilsError.invalidUrl(urlString)
}
let deferredBilling = try ApplePayUtils.createDeferredPaymentSummaryItem(item: params["deferredBilling"] as? [String: Any] ?? [:])
let request = PKDeferredPaymentRequest(paymentDescription: description, deferredBilling: deferredBilling, managementURL: url)
request.billingAgreement = params["billingAgreement"] as? String
if let tokenNotificationURL = params["tokenNotificationURL"] as? String {
request.tokenNotificationURL = URL(string: tokenNotificationURL)
}
if let freeCancellationTimestamp = params["freeCancellationDate"] as? Double {
request.freeCancellationDate = Date(timeIntervalSince1970: freeCancellationTimestamp)
if let tzIdentifier = params["freeCancellationDateTimeZone"] as? String {
request.freeCancellationDateTimeZone = TimeZone(identifier: tzIdentifier)
}
}
return request
}

@available(iOS 16.0, *)
internal class func buildPaymentTokenContexts(items: [[String: Any]]) -> [PKPaymentTokenContext] {
var result: [PKPaymentTokenContext] = []
Expand Down Expand Up @@ -437,6 +463,12 @@ extension PKPaymentRequest {
self.automaticReloadPaymentRequest = try ApplePayUtils.buildAutomaticReloadPaymentRequest(params: requestParams)
case "MultiMerchant":
self.multiTokenContexts = ApplePayUtils.buildPaymentTokenContexts(items: requestParams["merchants"] as? [[String: Any]] ?? [])
case "Deferred":
if #available(iOS 16.4, *) {
self.deferredPaymentRequest = try ApplePayUtils.buildDeferredPaymentRequest(params: requestParams)
} else {
throw ApplePayUtilsError.invalidRequestType("Deferred (requires iOS 16.4+)")
}
default:
throw ApplePayUtilsError.invalidRequestType(String(describing: requestParams["type"]))
}
Expand Down
2 changes: 1 addition & 1 deletion ios/StripeSdkImpl+PaymentSheet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,7 @@ extension StripeSdkImpl {
}()
return PaymentSheet.ApplePayConfiguration.Handlers(paymentRequestHandler: { request in
do {
try request.configureRequestType(requestParams: applePayParams)
try request.configureRequestType(requestParams: applePayParams["request"] as? NSDictionary)
} catch {
// At this point, we can't resolve a promise with an error object, so our best option is to create a redbox error
RCTMakeAndLogError(error.localizedDescription, nil, nil)
Expand Down
159 changes: 159 additions & 0 deletions ios/Tests/ApplePayUtilsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,138 @@ class ApplePayUtilsTests: XCTestCase {
XCTAssertEqual(result[0].rawValue, "customNetwork")
}

// MARK: - configureRequestType Tests

@available(iOS 16.0, *)
func test_configureRequestType_recurringType_configuresRecurringRequest() throws {
let request = PKPaymentRequest()
try request.configureRequestType(requestParams: TestFixtures.RECURRING_REQUEST_PARAMS)
XCTAssertNotNil(request.recurringPaymentRequest)
}

@available(iOS 16.4, *)
func test_configureRequestType_deferredType_configuresDeferredRequest() throws {
let request = PKPaymentRequest()
try request.configureRequestType(requestParams: TestFixtures.DEFERRED_REQUEST_PARAMS)
XCTAssertNotNil(request.deferredPaymentRequest)
}

@available(iOS 16.0, *)
func test_configureRequestType_nilParams_doesNotThrow() throws {
let request = PKPaymentRequest()
XCTAssertNoThrow(try request.configureRequestType(requestParams: nil))
}

@available(iOS 16.0, *)
func test_configureRequestType_invalidType_throws() {
let request = PKPaymentRequest()
XCTAssertThrowsError(
try request.configureRequestType(requestParams: ["type": "InvalidType"])
) { error in
XCTAssertEqual(
error as! ApplePayUtilsError, ApplePayUtilsError.invalidRequestType("Optional(\"InvalidType\")")
)
}
}

// MARK: - buildDeferredPaymentRequest Tests

@available(iOS 16.4, *)
func test_buildDeferredPaymentRequest_validParams_succeeds() throws {
let result = try ApplePayUtils.buildDeferredPaymentRequest(params: TestFixtures.DEFERRED_REQUEST_PARAMS)
XCTAssertEqual(result.paymentDescription, "Test deferred payment")
XCTAssertEqual(result.managementURL, URL(string: "https://example.com/manage")!)
XCTAssertEqual(result.billingAgreement, "You agree to be charged.")
XCTAssertNotNil(result.deferredBilling)
XCTAssertEqual(result.deferredBilling.label, "Test")
XCTAssertEqual(result.deferredBilling.amount, NSDecimalNumber(string: "9.99"))
}

@available(iOS 16.4, *)
func test_buildDeferredPaymentRequest_missingDescription_throws() {
let params: NSDictionary = [
"managementUrl": "https://example.com/manage",
"deferredBilling": [
"paymentType": "Deferred",
"label": "Test",
"amount": "9.99",
"deferredDate": 1700000000 as NSNumber,
] as [String: Any],
]
XCTAssertThrowsError(
try ApplePayUtils.buildDeferredPaymentRequest(params: params)
) { error in
XCTAssertEqual(
error as! ApplePayUtilsError, ApplePayUtilsError.missingParameter(nil, "paymentDescription")
)
}
}

@available(iOS 16.4, *)
func test_buildDeferredPaymentRequest_missingManagementUrl_throws() {
let params: NSDictionary = [
"paymentDescription": "Test deferred payment",
"deferredBilling": [
"paymentType": "Deferred",
"label": "Test",
"amount": "9.99",
"deferredDate": 1700000000 as NSNumber,
] as [String: Any],
]
XCTAssertThrowsError(
try ApplePayUtils.buildDeferredPaymentRequest(params: params)
) { error in
XCTAssertEqual(
error as! ApplePayUtilsError, ApplePayUtilsError.missingParameter(nil, "managementUrl")
)
}
}

@available(iOS 16.4, *)
func test_buildDeferredPaymentRequest_invalidUrl_throws() {
let params: NSDictionary = [
"paymentDescription": "Test deferred payment",
"managementUrl": "",
"deferredBilling": [
"paymentType": "Deferred",
"label": "Test",
"amount": "9.99",
"deferredDate": 1700000000 as NSNumber,
] as [String: Any],
]
XCTAssertThrowsError(
try ApplePayUtils.buildDeferredPaymentRequest(params: params)
) { error in
XCTAssertEqual(
error as! ApplePayUtilsError, ApplePayUtilsError.invalidUrl("")
)
}
}

@available(iOS 16.4, *)
func test_buildDeferredPaymentRequest_optionalFields() throws {
let params: NSDictionary = [
"type": "Deferred",
"paymentDescription": "Test deferred payment",
"managementUrl": "https://example.com/manage",
"billingAgreement": "You agree to be charged.",
"tokenNotificationURL": "https://example.com/notify",
"freeCancellationDate": 1700000000.0,
"freeCancellationDateTimeZone": "America/New_York",
"deferredBilling": [
"paymentType": "Deferred",
"label": "Test",
"amount": "9.99",
"deferredDate": 1700000000 as NSNumber,
] as [String: Any],
]
let result = try ApplePayUtils.buildDeferredPaymentRequest(params: params)
XCTAssertEqual(result.billingAgreement, "You agree to be charged.")
XCTAssertEqual(result.tokenNotificationURL, URL(string: "https://example.com/notify")!)
XCTAssertEqual(result.freeCancellationDate, Date(timeIntervalSince1970: 1700000000))
XCTAssertEqual(result.freeCancellationDateTimeZone, TimeZone(identifier: "America/New_York"))
}

private struct TestFixtures {
static let MERCHANT_ID = "merchant.com.id"
static let COUNTRY_CODE = "US"
Expand Down Expand Up @@ -454,5 +586,32 @@ class ApplePayUtilsTests: XCTestCase {
"label": "immediate label",
"amount": "2.00",
] as [String: Any]

static let DEFERRED_REQUEST_PARAMS: NSDictionary = [
"type": "Deferred",
"paymentDescription": "Test deferred payment",
"managementUrl": "https://example.com/manage",
"billingAgreement": "You agree to be charged.",
"deferredBilling": [
"paymentType": "Deferred",
"label": "Test",
"amount": "9.99",
"deferredDate": 1700000000 as NSNumber,
] as [String: Any],
]

static let RECURRING_REQUEST_PARAMS: NSDictionary = [
"type": "Recurring",
"description": "Test recurring payment",
"managementUrl": "https://example.com/manage",
"billingAgreement": "Monthly subscription.",
"billing": [
"paymentType": "Recurring",
"label": "Monthly",
"amount": "9.99",
"intervalUnit": "month",
"intervalCount": 1,
] as [String: Any],
]
}
}