1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142//
// PaymentMethodManagementViewController.swift
// hyperSwitch
//
// Created by Kuntimaddi Manideep on 19/12/25.
//
import Foundation
import WebKit
import Combine
import SwiftUI
class PaymentMethodManagementViewController: UIViewController {
@ObservedObject var hyperViewModel = HyperViewModel()
private var paymentSession: PaymentSession?
private var cancellables = Set<AnyCancellable>()
private let topBarView = UIView()
private let textLabel = UILabel()
private let backButton = UIButton(type: .custom)
private func setupPaymentWidget(onAddPaymentMethod: @escaping () -> Void) {
guard hyperViewModel.paymentSession != nil else { return }
lazy var paymentWidget = PaymentMethodManagementWidget(
onAddPaymentMethod: onAddPaymentMethod,
completion: { result in
switch result {
case .failed(let error):
print("Payment Method Management failed: \(error)")
case .closed:
print("Payment Method Management closed.")
}
}
)
view.addSubview(paymentWidget)
paymentWidget.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
paymentWidget.leadingAnchor.constraint(equalTo: view.leadingAnchor),
paymentWidget.trailingAnchor.constraint(equalTo: view.trailingAnchor),
paymentWidget.topAnchor.constraint(equalTo: topBarView.bottomAnchor),
paymentWidget.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
}
private func asyncBindPaymentManagementWidget(onAddPaymentMethod: @escaping () -> Void) {
hyperViewModel.$status
.receive(on: DispatchQueue.main)
.sink { [weak self] status in
switch status {
case .loading:
print("Loading payment method management session...")
case .success:
self?.setupPaymentWidget(onAddPaymentMethod: onAddPaymentMethod)
case .failure(let error):
print("Failed to prepare payment method management session: \(error)")
}
}
.store(in: &cancellables)
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .green
viewFrame()
hyperViewModel.preparePaymentMethodManagement()
asyncBindPaymentManagementWidget(onAddPaymentMethod: onAddPaymentMethod)
}
@objc func onAddPaymentMethod() -> Void {
var configuration = PaymentSheet.Configuration()
configuration.primaryButtonLabel = "Purchase ($0.00)"
configuration.paymentSheetHeaderLabel = "Add payment method"
configuration.displaySavedPaymentMethods = false
var appearance = PaymentSheet.Appearance()
appearance.colors.background = UIColor(red: 0.96, green: 0.97, blue: 0.98, alpha: 1.00)
appearance.primaryButton.cornerRadius = 32
configuration.appearance = appearance
self.hyperViewModel.paymentSession?.presentPaymentSheet(viewController: self, configuration: configuration, completion: { result in
DispatchQueue.main.async {
switch result {
case .completed:
self.showAlert(title: "Success", message: "Successfully saved the payment method")
self.hyperViewModel.preparePaymentMethodManagement()
case .failed(let error):
self.showAlert(title: "Error", message: "Failure: \(error.localizedDescription)")
self.hyperViewModel.preparePaymentMethodManagement()
case .canceled:
break
}
}
})
}
@objc func backButtonTapped() {
self.dismiss(animated: true, completion: nil)
}
}
extension PaymentMethodManagementViewController {
func viewFrame() {
view.addSubview(topBarView)
topBarView.translatesAutoresizingMaskIntoConstraints = false
topBarView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
topBarView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
topBarView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
topBarView.heightAnchor.constraint(equalToConstant: 65).isActive = true
backButton.setImage(UIImage(systemName: "chevron.left"), for: .normal)
backButton.addTarget(self, action: #selector(backButtonTapped), for: .touchUpInside)
topBarView.addSubview(backButton)
backButton.translatesAutoresizingMaskIntoConstraints = false
backButton.topAnchor.constraint(equalTo: topBarView.topAnchor, constant: 20).isActive = true
backButton.leadingAnchor.constraint(equalTo: topBarView.leadingAnchor, constant: 10).isActive = true
backButton.widthAnchor.constraint(equalToConstant: 25).isActive = true
backButton.heightAnchor.constraint(equalToConstant: 25).isActive = true
textLabel.text = "Hyperswitch"
textLabel.font = .boldSystemFont(ofSize: 16.5)
topBarView.addSubview(textLabel)
textLabel.translatesAutoresizingMaskIntoConstraints = false
textLabel.topAnchor.constraint(equalTo: topBarView.topAnchor, constant: 23.5).isActive = true
textLabel.leadingAnchor.constraint(equalTo: backButton.trailingAnchor, constant: 4).isActive = true
textLabel.heightAnchor.constraint(equalToConstant: 20).isActive = true
}
}
extension PaymentMethodManagementViewController {
func showAlert(title: String, message: String) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alertController.addAction(okAction)
DispatchQueue.main.async {
self.present(alertController, animated: true, completion: nil)
}
}
}