I'm trying to make a bridge to implement apple pay in react native. I've made two classes, one that will be expose to react native and the other one that implement apple pay methods. My issue is that when I try to build my app, the build failed with the error:
Cannot find protocol declaration for 'PKPaymentAuthorizationControllerDelegate'
that popped in the interface in mobile-swift.h:
SWIFT_CLASS("_TtC7Staging23InternalApplePayHandler")
@interface InternalApplePayHandler : NSObject
- (nonnull instancetype)init SWIFT_UNAVAILABLE;
+ (nonnull instancetype)new SWIFT_UNAVAILABLE_MSG("-init is unavailable");
@end
@class PKPaymentAuthorizationController;
@class PKPayment;
@class PKPaymentAuthorizationResult;
@interface InternalApplePayHandler (SWIFT_EXTENSION(Staging)) <PKPaymentAuthorizationControllerDelegate>
- (void)paymentAuthorizationController:(PKPaymentAuthorizationController * _Nonnull)controller didAuthorizePayment:(PKPayment * _Nonnull)payment handler:(void (^ _Nonnull)(PKPaymentAuthorizationResult * _Nonnull))completion;
- (void)paymentAuthorizationControllerDidFinish:(PKPaymentAuthorizationController * _Nonnull)controller;
@end
This is the class that implement it:
//
// ApplePayHandler.swift
// Mobile
//
// Created by me on 06/06/2025.
//
final class InternalApplePayHandler: NSObject {
private var paymentController: PKPaymentAuthorizationController?
private var onSuccess: ((String) -> Void)?
private var onFailure: ((String, String) -> Void)?
private var amount: NSNumber
private var currencyCode: String
private var countryCode: String
static func canMakePayment() -> Bool {
return PKPaymentAuthorizationController.canMakePayments(usingNetworks: [.visa, .masterCard, .amex, .cartesBancaires])
}
init(
amount: NSNumber,
currencyCode: String,
countryCode: String,
onSuccess: @escaping (String) -> Void,
onFailure: @escaping (String, String) -> Void
) {
self.amount = amount
self.currencyCode = currencyCode
self.countryCode = countryCode
self.onSuccess = onSuccess
self.onFailure = onFailure
}
func start() {
let request = PKPaymentRequest()
request.merchantIdentifier = getMerchantIdentifier()
request.countryCode = countryCode
request.currencyCode = currencyCode
request.supportedNetworks = [.visa, .masterCard, .amex, .discover]
request.merchantCapabilities = .threeDSecure
request.paymentSummaryItems = [
PKPaymentSummaryItem(label: "Total", amount: NSDecimalNumber(decimal: amount.decimalValue), type: .final)
]
let controller = PKPaymentAuthorizationController(paymentRequest: request)
controller.delegate = self
paymentController = controller
controller.present { success in
if !success {
self.onFailure?("NOT_PRESENTED", "Failed to present Apple Pay sheet")
self.cleanup()
}
}
}
private func getMerchantIdentifier() -> String {
let bundleId = Bundle.main.bundleIdentifier ?? ""
return bundleId == "MERCHANT_ID" ? "MERCHANT_ID" : "MERCHANT_ID_B"
}
private func cleanup() {
paymentController = nil
onSuccess = nil
onFailure = nil
}
}
extension InternalApplePayHandler: PKPaymentAuthorizationControllerDelegate {
func paymentAuthorizationController(_ controller: PKPaymentAuthorizationController, didAuthorizePayment payment: PKPayment, handler completion: @escaping (PKPaymentAuthorizationResult) -> Void) {
let tokenData = payment.token.paymentData.base64EncodedString()
onSuccess?(tokenData)
completion(PKPaymentAuthorizationResult(status: .success, errors: nil))
cleanup()
}
func paymentAuthorizationControllerDidFinish(_ controller: PKPaymentAuthorizationController) {
controller.dismiss {
self.onFailure?("CANCELLED", "User cancelled Apple Pay")
self.cleanup()
}
}
}
and this class is used in the other class:
//
// ApplePayManager.swift
// Mobile
//
// Created by me on 03/06/2025.
//
import Foundation
import React
import PassKit
@objc(ApplePayManager)
class ApplePayManager: NSObject {
private var paymentHandler: InternalApplePayHandler?
@objc
func canMakePayment(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
resolve(InternalApplePayHandler.canMakePayment())
}
@objc
func startPayment(_ amount: NSNumber, currencyCode: String, countryCode: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
paymentHandler = InternalApplePayHandler(
amount: amount,
currencyCode: currencyCode,
countryCode: countryCode,
onSuccess: { token in
resolve(token)
},
onFailure: { code, message in
reject(code, message, nil)
}
)
paymentHandler?.start()
}
@objc
static func requiresMainQueueSetup() -> Bool {
return true
}
}
and finally this is my ApplePayManager.m:
#import <React/RCTBridgeModule.h>
#import <React/RCTUtils.h>
#import <PassKit/PassKit.h>
#import <PassKit/PKPaymentAuthorizationController.h>
@interface RCT_EXTERN_MODULE(ApplePayManager, NSObject)
RCT_EXTERN_METHOD(canMakePayment:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(startPayment:(nonnull NSNumber *)amount
currencyCode:(nonnull NSString *)currencyCode
countryCode:(nonnull NSString *)countryCode
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
@end
and I also imported the passKit inside my bridging header
#import <PassKit/PassKit.h>