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
Binary file added .DS_Store
Binary file not shown.
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
Binary file added pay_ios/.DS_Store
Binary file not shown.
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
102 changes: 100 additions & 2 deletions pay_ios/ios/Classes/PaymentHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,13 @@ 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
var creditSurchargeLabel: String?
/// Holds the current status of the payment process.
var paymentHandlerStatus: PaymentHandlerStatus!

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

self.creditSurchargeRate = .zero // Reset to 0 before every new payment
self.creditSurchargeLabel = nil // Reset custom label

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)

// Pull optional custom label from config
self.creditSurchargeLabel = processingFee["label"] as? String
}
}

// 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 +189,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 +204,62 @@ 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
// Use custom label from config if provided, otherwise generate default
let surchargeLabel: String
if let customLabel = self.creditSurchargeLabel {
surchargeLabel = customLabel
} else {
let percentage = self.creditSurchargeRate.multiplying(byPowerOf10: 2)
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 +292,17 @@ 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"
}
}
}