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
6 changes: 5 additions & 1 deletion pay/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@ dependencies:
flutter_localizations:
sdk: flutter
pay_android: ^3.1.1
pay_ios: ^1.1.0
pay_ios:
git:
url: https://github.com/dibsyhq/flutter-plugin.git
ref: main
path: pay_ios
pay_platform_interface: ^2.0.0
meta: ^1.10.0

Expand Down
3 changes: 3 additions & 0 deletions pay_ios/ios/Classes/PaymentExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,9 @@ extension PKPaymentNetwork {
case "girocard":
guard #available(iOS 14.0, *) else { return nil }
return .girocard
case "himyan":
guard #available(iOS 18.4, *) else { return nil }
return .himyan
case "idCredit":
return .idCredit
case "interac":
Expand Down
93 changes: 91 additions & 2 deletions pay_ios/ios/Classes/PaymentHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,12 @@ enum PaymentHandlerStatus {
/// paymentHandler.canMakePayments(stringArguments)
/// ```
class PaymentHandler: NSObject {

// CHANGE 1: Define the variable here so the class owns it.
// This stores the original prices without any surcharges.
var baseSummaryItems: [PKPaymentSummaryItem] = []

var creditSurchargeRate: NSDecimalNumber = .zero
/// Holds the current status of the payment process.
var paymentHandlerStatus: PaymentHandlerStatus!

Expand Down Expand Up @@ -74,12 +79,25 @@ class PaymentHandler: NSObject {
// Reset payment handler status
paymentHandlerStatus = .started

self.creditSurchargeRate = .zero // Reset to 0 before every new payment

if let configDict = PaymentHandler.extractPaymentConfiguration(from: paymentConfiguration) {
if let processingFee = configDict["processingFee"] as? [String: Any],
let creditRate = processingFee["credit"] as? Double {

// Convert "2.5" (percentage) to "0.025" (multiplier)
let multiplier = creditRate / 100.0
self.creditSurchargeRate = NSDecimalNumber(value: multiplier)
}
}

// Deserialize payment configuration.
guard let paymentRequest = PaymentHandler.createPaymentRequest(from: paymentConfiguration, paymentItems: paymentItems) else {
result(FlutterError(code: "invalidPaymentConfiguration", message: "It was not possible to create a payment request from the provided configuration. Review your payment configuration and run again", details: nil))
return
}

self.baseSummaryItems = paymentRequest.paymentSummaryItems
// Display the payment selector with the request created.
let paymentController = PKPaymentAuthorizationController(paymentRequest: paymentRequest)
paymentController.delegate = self
Expand Down Expand Up @@ -166,6 +184,13 @@ class PaymentHandler: NSObject {
if let supportedNetworks = supportedNetworks(from: paymentConfigurationString) {
paymentRequest.supportedNetworks = supportedNetworks
}

// Add supported countries if available (iOS 11+).
if #available(iOS 11.0, *) {
if let supportedCountries = paymentConfiguration["supportedCountries"] as? Array<String> {
paymentRequest.supportedCountries = Set(supportedCountries)
}
}

return paymentRequest
}
Expand All @@ -174,8 +199,57 @@ class PaymentHandler: NSObject {
/// Extension that implements the completion methods in the delegate to respond to user selection.
extension PaymentHandler: PKPaymentAuthorizationControllerDelegate {

func paymentAuthorizationControllerWillAuthorizePayment(_ controller: PKPaymentAuthorizationController) {
paymentHandlerStatus = .authorizationStarted
func paymentAuthorizationController(_ controller: PKPaymentAuthorizationController, didSelectPaymentMethod paymentMethod: PKPaymentMethod, handler completion: @escaping (PKPaymentRequestPaymentMethodUpdate) -> Void) {

let isCredit = paymentMethod.type == .credit

// 1. Start with the clean, original list of items
var currentItems = self.baseSummaryItems

if isCredit && self.creditSurchargeRate.compare(NSDecimalNumber.zero) == .orderedDescending {

if let originalTotalItem = currentItems.last {

let originalAmount = originalTotalItem.amount

// 2. Define Rounding Behavior (Bankers rounding or Plain)
let behavior = NSDecimalNumberHandler(roundingMode: .plain,
scale: 2,
raiseOnExactness: false,
raiseOnOverflow: false,
raiseOnUnderflow: false,
raiseOnDivideByZero: false)

// 3. Calculate the Surcharge Amount (e.g., 100 * 0.025 = 2.5)
let rawSurcharge = originalAmount.multiplying(by: self.creditSurchargeRate)
let surchargeAmount = rawSurcharge.rounding(accordingToBehavior: behavior)

// 4. Create the Surcharge Line Item
// Optional: Format the percentage for the label (e.g. "Credit Card Fee (2.5%)")
let percentage = self.creditSurchargeRate.multiplying(byPowerOf10: 2)
let surchargeLabel = "Credit Card Fee (\(percentage.stringValue)%)"

let surchargeItem = PKPaymentSummaryItem(label: surchargeLabel, amount: surchargeAmount)
// Note: If you want to show it as an additional cost, usually types are final.
surchargeItem.type = .final

// 5. Calculate New Grand Total (Original + Surcharge)
let newTotalAmount = originalAmount.adding(surchargeAmount)
let newTotalItem = PKPaymentSummaryItem(label: originalTotalItem.label, amount: newTotalAmount)

// 6. Construct the new list
// Remove the old total
currentItems.removeLast()

// Add the surcharge line item
currentItems.append(surchargeItem)

// Add the new Grand Total at the very end (Apple Pay requirement)
currentItems.append(newTotalItem)
}
}

completion(PKPaymentRequestPaymentMethodUpdate(paymentSummaryItems: currentItems))
}

func paymentAuthorizationController(_: PKPaymentAuthorizationController, didAuthorizePayment payment: PKPayment, handler completion: @escaping (PKPaymentAuthorizationResult) -> Void) {
Expand Down Expand Up @@ -208,3 +282,18 @@ extension PaymentHandler: PKPaymentAuthorizationControllerDelegate {
}
}
}


extension PKPaymentMethodType {
var stringValue: String {
switch self {
case .debit: return "debit"
case .credit: return "credit"
case .prepaid: return "prepaid"
case .store: return "store"
case .eMoney: return "emoney"
default: return "unknown"
}
}
}