skipcash-reactnative 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,994 @@
1
+ // SetupVC.swift
2
+ // SkipCashSDK
3
+ //
4
+ // By Ahmed Mustafa on 3/04/24.
5
+
6
+ import React
7
+ import Foundation
8
+ import UIKit
9
+ import CryptoKit
10
+ import CommonCrypto
11
+ import PassKit
12
+ import WebKit
13
+
14
+ public struct ResultObject: Codable {
15
+ public var isSuccess: Bool
16
+ var data: String
17
+ }
18
+
19
+ public class ResponseData: NSObject, Codable {
20
+ public var transactionId: String?
21
+ public var paymentId: String?
22
+ public var resultObj: ResultObject
23
+ public var returnCode: Int
24
+ public var errorCode: Int
25
+ public var errorMessage: String?
26
+ public var error: String?
27
+ public var validationErrors: String?
28
+ public var hasError: Bool
29
+ public var hasValidationError: Bool
30
+
31
+ // Initializer
32
+ public init(transactionId: String?, paymentId: String?, resultObj: ResultObject, returnCode: Int, errorCode: Int, errorMessage: String?, error: String?, validationErrors: String?, hasError: Bool, hasValidationError: Bool) {
33
+ self.transactionId = transactionId
34
+ self.paymentId = paymentId
35
+ self.resultObj = resultObj
36
+ self.returnCode = returnCode
37
+ self.errorCode = errorCode
38
+ self.errorMessage = errorMessage
39
+ self.error = error
40
+ self.validationErrors = validationErrors
41
+ self.hasError = hasError
42
+ self.hasValidationError = hasValidationError
43
+ }
44
+
45
+
46
+ public func getResultObjIsSuccess() -> Bool {
47
+ return resultObj.isSuccess;
48
+ }
49
+
50
+ public func getPaymentId() -> String {
51
+ return paymentId ?? "No Payment ID";
52
+ }
53
+
54
+ public func getTransactionId() -> String {
55
+ return transactionId ?? "No Transaction ID";
56
+ }
57
+
58
+ public func getErrorMessage() -> String {
59
+ return errorMessage ?? "Nothing to show";
60
+ }
61
+ }
62
+
63
+ @objc public protocol ApplePayReponseDelegate{
64
+ func applePayResponseData(transactionID: String, paymentID: String, isSuccess: Bool, token: String, returnCode: Int, errorMessage: String, completion: ((PKPaymentAuthorizationResult) -> Void)?)
65
+ }
66
+
67
+ public class SetupVC: UIViewController {
68
+ let production = "https://api.skipcash.app"
69
+ let sandboxUrl = "https://skipcashtest.azurewebsites.net"
70
+
71
+ public var paymentToken: String = ""
72
+ public var paymentID: String = ""
73
+ public var transactionID: String = ""
74
+ public var delegate:ApplePayReponseDelegate?
75
+ public var completion: ((PKPaymentAuthorizationResult) -> Void)?
76
+
77
+ override public func viewDidLoad() {
78
+ self.callApplePay(paymentId: self.paymentID, applePaymentToken: self.paymentToken)
79
+ }
80
+
81
+ func callApplePay(paymentId: String, applePaymentToken: String) {
82
+
83
+ let request = "{\"token\": {\"paymentData\":\(applePaymentToken)}}"
84
+ let postData = request.data(using: .utf8)
85
+ let url = URL(string: production + "/api/v1/payments/apple/" + paymentId + "/pay-mc")!
86
+ var urlRequest = URLRequest(url: url)
87
+ urlRequest.httpMethod = "POST"
88
+ urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
89
+ urlRequest.httpBody = postData
90
+
91
+ let task: URLSessionDataTask
92
+
93
+ task = URLSession.shared.dataTask(with: urlRequest) { data, response, error in
94
+
95
+ guard let data = data else {
96
+ debugPrint(data ?? "NO DATA RECEIVED")
97
+ let responseData = ResponseData(
98
+ transactionId: self.transactionID,
99
+ paymentId: self.paymentID,
100
+ resultObj: ResultObject(isSuccess: false, data: "\(applePaymentToken)"),
101
+ returnCode: 400,
102
+ errorCode: 1001,
103
+ errorMessage: "Sample error message",
104
+ error: "No data received",
105
+ validationErrors: "Sample validation errors",
106
+ hasError: true,
107
+ hasValidationError: true
108
+ )
109
+
110
+ DispatchQueue.main.async{
111
+ self.dismiss(animated: true, completion: nil)
112
+ }
113
+
114
+ DispatchQueue.main.asyncAfter(deadline: .now()){
115
+ self.delegate?.applePayResponseData(transactionID: self.transactionID, paymentID: self.paymentID, isSuccess: responseData.resultObj.isSuccess, token: responseData.resultObj.data, returnCode: responseData.returnCode, errorMessage: responseData.getErrorMessage(), completion: self.completion)
116
+ }
117
+
118
+ return
119
+ }
120
+
121
+ if let text = String(data: data, encoding: .utf8) {
122
+ if let jsonData = text.data(using: .utf8) {
123
+ do {
124
+ let decoder = JSONDecoder()
125
+ var responseData = try decoder.decode(ResponseData.self, from: jsonData)
126
+
127
+ responseData.paymentId = paymentId
128
+
129
+ DispatchQueue.main.async{
130
+ self.dismiss(animated: true, completion: nil)
131
+ }
132
+
133
+ DispatchQueue.main.asyncAfter(deadline: .now()){
134
+ self.delegate?.applePayResponseData(transactionID: self.transactionID, paymentID: self.paymentID, isSuccess: responseData.resultObj.isSuccess, token: responseData.resultObj.data, returnCode: responseData.returnCode, errorMessage: responseData.getErrorMessage(), completion: self.completion)
135
+ }
136
+
137
+ } catch {
138
+ let responseData = ResponseData(
139
+ transactionId: self.transactionID,
140
+ paymentId: self.paymentID,
141
+ resultObj: ResultObject(isSuccess: false, data: "\(applePaymentToken)"),
142
+ returnCode: 400,
143
+ errorCode: 1001,
144
+ errorMessage: "Payment Failed!",
145
+ error: "\(error)",
146
+ validationErrors: "failed to decode json response for the payment",
147
+ hasError: true,
148
+ hasValidationError: true
149
+ )
150
+
151
+
152
+ DispatchQueue.main.async{
153
+ self.dismiss(animated: true, completion: nil)
154
+ }
155
+
156
+ DispatchQueue.main.asyncAfter(deadline: .now()){
157
+
158
+ self.delegate?.applePayResponseData(transactionID: self.transactionID, paymentID: self.paymentID, isSuccess: responseData.resultObj.isSuccess, token: responseData.resultObj.data, returnCode: responseData.returnCode, errorMessage: responseData.getErrorMessage(), completion: self.completion)
159
+ }
160
+
161
+
162
+ }
163
+ }
164
+ else {
165
+ let responseData = ResponseData(
166
+ transactionId: self.transactionID,
167
+ paymentId: self.paymentID,
168
+ resultObj: ResultObject(isSuccess: false, data: "\(applePaymentToken)"),
169
+ returnCode: 400,
170
+ errorCode: 1001,
171
+ errorMessage: "Sample error message",
172
+ error: "Failed to convert JSON text to data",
173
+ validationErrors: "Sample validation errors",
174
+ hasError: true,
175
+ hasValidationError: true
176
+ )
177
+
178
+ DispatchQueue.main.async{
179
+ self.dismiss(animated: true, completion: nil)
180
+ }
181
+
182
+ DispatchQueue.main.asyncAfter(deadline: .now()){
183
+ self.delegate?.applePayResponseData(transactionID: self.transactionID, paymentID: self.paymentID, isSuccess: responseData.resultObj.isSuccess, token: responseData.resultObj.data, returnCode: responseData.returnCode, errorMessage: responseData.getErrorMessage(), completion: self.completion)
184
+ }
185
+ }
186
+ }
187
+ else {
188
+ let responseData = ResponseData(
189
+ transactionId: self.transactionID,
190
+ paymentId: self.paymentID,
191
+ resultObj: ResultObject(isSuccess: false, data: "\(applePaymentToken)"),
192
+ returnCode: 400,
193
+ errorCode: 1001,
194
+ errorMessage: "Sample error message",
195
+ error: "Failed to decode data as UTF-8 string",
196
+ validationErrors: "Sample validation errors",
197
+ hasError: true,
198
+ hasValidationError: true
199
+ )
200
+
201
+ DispatchQueue.main.async{
202
+ self.dismiss(animated: true, completion: nil)
203
+ }
204
+
205
+ DispatchQueue.main.asyncAfter(deadline: .now()){
206
+ self.delegate?.applePayResponseData(transactionID: self.transactionID, paymentID: self.paymentID, isSuccess: responseData.resultObj.isSuccess, token: responseData.resultObj.data, returnCode: responseData.returnCode, errorMessage: responseData.getErrorMessage(), completion: self.completion)
207
+ }
208
+ }
209
+ }
210
+ task.resume()
211
+ }
212
+ }
213
+
214
+ public class PaymentData: NSObject, Codable{
215
+ let merchantIdentifier: String
216
+ let countryCode: String
217
+ let currencyCode: String
218
+ let createPaymentLinkEndPoint: String
219
+ let authorizationHeader: String
220
+ let amount: String
221
+ let firstName: String
222
+ let lastName: String
223
+ let phone: String
224
+ let email: String
225
+ let summaryItems: [String: String]
226
+
227
+ let returnUrl: String
228
+ let transactionId: String
229
+ let webhookUrl: String
230
+
231
+ let custom1: String
232
+ let custom2: String
233
+ let custom3: String
234
+ let custom4: String
235
+ let custom5: String
236
+ let custom6: String
237
+ let custom7: String
238
+ let custom8: String
239
+ let custom9: String
240
+ let custom10: String
241
+
242
+ let Description: String
243
+ let Subject: String
244
+ let paymentModalTitle: String
245
+
246
+ let merchantName: String
247
+
248
+ init?(data: [String: Any]) {
249
+ guard
250
+ let merchantIdentifier = data["merchantIdentifier"] as? String,
251
+ let countryCode = data["countryCode"] as? String,
252
+ let currencyCode = data["currencyCode"] as? String,
253
+ let createPaymentLinkEndPoint = data["createPaymentLinkEndPoint"] as? String,
254
+ let authorizationHeader = data["authorizationHeader"] as? String,
255
+ let amount = data["amount"] as? String,
256
+ let firstName = data["firstName"] as? String,
257
+ let lastName = data["lastName"] as? String,
258
+ let phone = data["phone"] as? String,
259
+ let email = data["email"] as? String,
260
+ let summaryItems = data["summaryItems"] as? [String: String],
261
+
262
+ let returnURL = data["returnUrl"] as? String, //
263
+ let transactionId = data["transactionId"] as? String,
264
+ let webhookUrl = data["webhookUrl"] as? String,
265
+ let returnUrl = data["returnUrl"] as? String,
266
+ let custom1 = data["custom1"] as? String, let custom2 = data["custom2"] as? String,
267
+ let custom3 = data["custom3"] as? String, let custom4 = data["custom4"] as? String,
268
+ let custom5 = data["custom5"] as? String, let custom6 = data["custom6"] as? String,
269
+ let custom7 = data["custom7"] as? String, let custom8 = data["custom8"] as? String,
270
+ let custom9 = data["custom9"] as? String, let custom10 = data["custom10"] as? String,
271
+ let Description = data["description"] as? String,
272
+ let Subject = data["subject"] as? String,
273
+ let paymentModalTitle = data["paymentModalTitle"] as? String,
274
+
275
+ let merchantName = data["merchantName"] as? String
276
+ else {
277
+ return nil
278
+ }
279
+
280
+ self.merchantIdentifier = merchantIdentifier
281
+ self.countryCode = countryCode
282
+ self.currencyCode = currencyCode
283
+ self.createPaymentLinkEndPoint = createPaymentLinkEndPoint
284
+ self.authorizationHeader = authorizationHeader
285
+ self.amount = amount
286
+ self.firstName = firstName
287
+ self.lastName = lastName
288
+ self.phone = phone
289
+ self.email = email
290
+ self.summaryItems = summaryItems
291
+
292
+ self.returnUrl = returnUrl
293
+ self.transactionId = transactionId
294
+ self.webhookUrl = webhookUrl
295
+ self.custom1 = custom1
296
+ self.custom2 = custom2
297
+ self.custom3 = custom3
298
+ self.custom4 = custom4
299
+ self.custom5 = custom5
300
+ self.custom6 = custom6
301
+ self.custom7 = custom7
302
+ self.custom8 = custom8
303
+ self.custom9 = custom9
304
+ self.custom10 = custom10
305
+
306
+ self.Subject = Subject
307
+ self.Description = Description
308
+ self.paymentModalTitle = paymentModalTitle
309
+
310
+ self.merchantName = merchantName
311
+ }
312
+ }
313
+
314
+ @objc(SkipcashReactnative)
315
+ class SkipcashReactnative: RCTEventEmitter, ApplePayReponseDelegate {
316
+ private var paymentID: String = ""
317
+ private var transactionID: String = ""
318
+
319
+ var webView: WKWebView!
320
+ var navigationController: UINavigationController?
321
+ var activityIndicator: UIActivityIndicatorView!
322
+ var returnURL: String?
323
+
324
+
325
+ override func supportedEvents() -> [String]! {
326
+ return ["applepay_response", "responseScPaymentData"]
327
+ }
328
+
329
+
330
+ public func applePayResponseData(transactionID: String, paymentID: String, isSuccess: Bool, token: String, returnCode: Int, errorMessage: String, completion: ((PKPaymentAuthorizationResult) -> Void)?) {
331
+ let responseData: [String: Any] = [
332
+ "transactionId": transactionID,
333
+ "paymentId": paymentID,
334
+ "isSuccess": isSuccess,
335
+ "token": token,
336
+ "returnCode": returnCode,
337
+ "errorMessage": errorMessage
338
+ ]
339
+
340
+ if (isSuccess) {
341
+ let errors = [Error]()
342
+ let status = PKPaymentAuthorizationStatus.success
343
+ self.paymentStatus = status
344
+ completion?(PKPaymentAuthorizationResult(status: status, errors: errors))
345
+ }else{
346
+ let errors = [Error]()
347
+ let status = PKPaymentAuthorizationStatus.failure
348
+ self.paymentStatus = status
349
+ completion?(PKPaymentAuthorizationResult(status: status, errors: errors))
350
+ }
351
+ sendEvent(withName: "applepay_response", body: responseData)
352
+ }
353
+
354
+ @IBOutlet weak var applePayView: UIView!
355
+ var paymentController: PKPaymentAuthorizationController?
356
+ var paymentSummaryItems = [PKPaymentSummaryItem]()
357
+ var paymentStatus = PKPaymentAuthorizationStatus.failure
358
+ typealias PaymentCompletionHandler = (Bool) -> Void
359
+ var completionHandler: PaymentCompletionHandler!
360
+
361
+ static let supportedNetworks: [PKPaymentNetwork] = [
362
+ .amex,
363
+ .discover,
364
+ .masterCard,
365
+ .visa
366
+ ]
367
+
368
+ @objc(isWalletHasCards:rejecter:)
369
+ func isWalletHasCards (resolve:RCTPromiseResolveBlock,rejecter:RCTPromiseRejectBlock) -> Void {
370
+ let result = SkipcashReactnative.applePayStatus();
371
+
372
+ resolve(result.canMakePayments);
373
+ }
374
+
375
+ @objc(setupNewCard)
376
+ func setupNewCard() {
377
+ let passLibrary = PKPassLibrary()
378
+ passLibrary.openPaymentSetup()
379
+ }
380
+
381
+ class func applePayStatus() -> (canMakePayments: Bool, canSetupCards: Bool) {
382
+ return (PKPaymentAuthorizationController.canMakePayments(),
383
+ PKPaymentAuthorizationController.canMakePayments(usingNetworks: supportedNetworks))
384
+ }
385
+
386
+ func convertToDecimal(with string: String) -> NSDecimalNumber {
387
+ let formatter = NumberFormatter()
388
+ formatter.decimalSeparator = "."
389
+ formatter.generatesDecimalNumbers = true
390
+ formatter.maximumFractionDigits = 2
391
+
392
+ if let number = formatter.number(from: string) as? NSDecimalNumber {
393
+ return number
394
+ } else {
395
+ return NSDecimalNumber.zero
396
+ }
397
+ }
398
+
399
+ func getPaymentID(authorizationHeader: String, data: PaymentData, createPaymentApi: String, returnURL: String, completion: @escaping ([String: Any]?) -> Void) {
400
+
401
+ var convertedData: [String: Any] = [:]
402
+
403
+ convertedData["Amount"] = data.amount
404
+ convertedData["FirstName"] = data.firstName
405
+ convertedData["LastName"] = data.lastName
406
+ convertedData["Phone"] = data.phone
407
+ convertedData["Email"] = data.email
408
+
409
+ convertedData["ReturnUrl"] = returnURL
410
+ convertedData["TransactionId"] = data.transactionId
411
+ convertedData["WebhookUrl"] = data.webhookUrl
412
+
413
+ convertedData["Custom1"] = data.custom1
414
+ convertedData["Custom2"] = data.custom2
415
+ convertedData["Custom3"] = data.custom3
416
+ convertedData["Custom4"] = data.custom4
417
+ convertedData["Custom5"] = data.custom5
418
+ convertedData["Custom6"] = data.custom6
419
+ convertedData["Custom7"] = data.custom7
420
+ convertedData["Custom8"] = data.custom8
421
+ convertedData["Custom9"] = data.custom9
422
+ convertedData["Custom10"] = data.custom10
423
+
424
+ convertedData["Description"] = data.Description
425
+ convertedData["Subject"] = data.Subject
426
+
427
+
428
+ guard let jsonData = try? JSONSerialization.data(withJSONObject: convertedData) else {
429
+ completion(nil)
430
+ return
431
+ }
432
+
433
+ var request = URLRequest(url: URL(string: createPaymentApi)!, timeoutInterval: 30)
434
+ request.addValue("application/json", forHTTPHeaderField: "Content-Type")
435
+
436
+ if authorizationHeader.count > 0 {
437
+ request.addValue(authorizationHeader, forHTTPHeaderField: "Authorization")
438
+ }
439
+
440
+ request.httpMethod = "POST"
441
+ request.httpBody = jsonData
442
+
443
+ let task = URLSession.shared.dataTask(with: request) { data, response, error in
444
+ guard let data = data else {
445
+ if let error = error {
446
+ debugPrint("Error: \(error.localizedDescription)")
447
+ let responseData: [String: Any] = [
448
+ "transactionId": self.transactionID,
449
+ "paymentId": self.paymentID,
450
+ "isSuccess": false,
451
+ "token": "",
452
+ "returnCode": 400,
453
+ "errorMessage": "\(error.localizedDescription)"
454
+ ]
455
+
456
+ DispatchQueue.main.async {
457
+ self.sendEvent(withName: "applepay_response", body: responseData)
458
+ completion(nil)
459
+ }
460
+ return
461
+ }
462
+ return
463
+ }
464
+
465
+ do {
466
+ let json = try JSONSerialization.jsonObject(with: data, options: [])
467
+ if let tempResponse = json as? [String: Any],
468
+ let responseObj = tempResponse["resultObj"] as? [String: Any],
469
+ let paymentID = responseObj["id"] as? String, let payUrl = responseObj["payUrl"] as? String, let transactionId = responseObj["transactionId"] as? String{
470
+ DispatchQueue.main.async {
471
+ completion(["paymentID": paymentID, "payUrl": payUrl, "transactionId": transactionId])
472
+ }
473
+ return
474
+ }else{
475
+ if let tempResponse = json as? [String: Any],
476
+ let errorMessage = tempResponse["errorMessage"] as? String {
477
+
478
+ let responseData: [String: Any] = [
479
+ "transactionId": self.transactionID,
480
+ "paymentId": self.paymentID,
481
+ "isSuccess": false,
482
+ "token": "",
483
+ "returnCode": 400,
484
+ "errorMessage": errorMessage
485
+ ]
486
+
487
+ DispatchQueue.main.async {
488
+ self.sendEvent(withName: "applepay_response", body: responseData)
489
+ completion(nil)
490
+ }
491
+ }
492
+ }
493
+ } catch {
494
+ debugPrint("Error: \(error.localizedDescription) - endpoint response")
495
+ let responseData: [String: Any] = [
496
+ "transactionId": self.transactionID,
497
+ "paymentId": self.paymentID,
498
+ "isSuccess": false,
499
+ "token": "",
500
+ "returnCode": 400,
501
+ "errorMessage": "\(error.localizedDescription)"
502
+ ]
503
+
504
+ self.sendEvent(withName: "applepay_response", body: responseData)
505
+ completion(nil)
506
+ return
507
+ }
508
+ }
509
+
510
+ task.resume()
511
+ }
512
+
513
+ @objc(initiateApplePay:)
514
+ func initiateApplePay(jsonString: String){
515
+ if let jsonData = jsonString.data(using: .utf8) {
516
+ do {
517
+ // Convert JSON data to a dictionary
518
+ if let paymentDictionary = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] {
519
+ if let paymentData = PaymentData(data: paymentDictionary) {
520
+ getPaymentID(authorizationHeader: paymentData.authorizationHeader, data: paymentData, createPaymentApi: paymentData.createPaymentLinkEndPoint, returnURL: paymentData.returnUrl) { result in
521
+ if let result = result {
522
+ let paymentID = result["paymentID"] as? String ?? ""
523
+
524
+ if(paymentID.count == 0){
525
+ debugPrint("new payment request failed!")
526
+ return
527
+ }
528
+
529
+ self.startPayment(data: paymentData, paymentID: paymentID) { success in
530
+ if success {} else {
531
+ let responseData: [String: Any] = [
532
+ "transactionId": self.transactionID,
533
+ "paymentId": self.paymentID,
534
+ "isSuccess": false,
535
+ "token": "",
536
+ "returnCode": 400,
537
+ "errorMessage": "Payment failed!"
538
+ ]
539
+
540
+ self.sendEvent(withName: "applepay_response", body: responseData)
541
+ return
542
+ }
543
+ }
544
+ }
545
+ }
546
+ } else {
547
+ debugPrint("Unable to convert jsonData(cross-platform passed data) to paymentDictionary")
548
+ let responseData: [String: Any] = [
549
+ "transactionId": self.transactionID,
550
+ "paymentId": self.paymentID,
551
+ "isSuccess": false,
552
+ "token": "",
553
+ "returnCode": 400,
554
+ "errorMessage": ""
555
+ ]
556
+
557
+ self.sendEvent(withName: "applepay_response", body: responseData)
558
+ return
559
+ }
560
+ } else {
561
+ debugPrint("Error: Unable to convert JSON string to data")
562
+ let responseData: [String: Any] = [
563
+ "transactionId": self.transactionID,
564
+ "paymentId": self.paymentID,
565
+ "isSuccess": false,
566
+ "token": "",
567
+ "returnCode": 400,
568
+ "errorMessage": ""
569
+ ]
570
+ self.sendEvent(withName: "applepay_response", body: responseData)
571
+ return
572
+ }
573
+ } catch {
574
+ debugPrint("Error: \(error)")
575
+ let responseData: [String: Any] = [
576
+ "transactionId": self.transactionID,
577
+ "paymentId": self.paymentID,
578
+ "isSuccess": false,
579
+ "token": "",
580
+ "returnCode": 400,
581
+ "errorMessage": "Error: \(error)"
582
+ ]
583
+
584
+ self.sendEvent(withName: "applepay_response", body: responseData)
585
+ return
586
+ }
587
+ }
588
+
589
+ // else {
590
+ // debugPrint("Error: Unable to convert JSON string to data")
591
+ // let responseData: [String: Any] = [
592
+ // "transactionId": self.transactionID,
593
+ // "paymentId": self.paymentID,
594
+ // "isSuccess": false,
595
+ // "token": "",
596
+ // "returnCode": 400,
597
+ // "errorMessage": "Unable to convert JSON string to data"
598
+ // ]
599
+
600
+ // self.sendEvent(withName: "applepay_response", body: responseData)
601
+ // }
602
+ }
603
+
604
+ func startPayment(data: PaymentData, paymentID: String, completion: @escaping PaymentCompletionHandler) {
605
+
606
+ completionHandler = completion
607
+ self.paymentID = paymentID
608
+
609
+
610
+ var paymentSummaryItems = [PKPaymentSummaryItem]()
611
+
612
+ for (label, amountString) in data.summaryItems {
613
+ guard let amount = Decimal(string: amountString) else {
614
+ // Handle invalid amount string
615
+ debugPrint("Invalid amount string: \(amountString)")
616
+ continue
617
+ }
618
+
619
+ // Successfully converted, create a PKPaymentSummaryItem and append it
620
+ let paymentItem = PKPaymentSummaryItem(label: label, amount: NSDecimalNumber(decimal: amount))
621
+ paymentSummaryItems.append(paymentItem)
622
+ }
623
+
624
+
625
+ let totalAmount = convertToDecimal(with: data.amount)
626
+
627
+ let totalAmountItem = PKPaymentSummaryItem(label: data.merchantName, amount: totalAmount)
628
+ paymentSummaryItems.append(totalAmountItem)
629
+
630
+
631
+ let paymentRequest = PKPaymentRequest()
632
+
633
+ paymentRequest.paymentSummaryItems = paymentSummaryItems
634
+ paymentRequest.merchantIdentifier = data.merchantIdentifier
635
+ paymentRequest.merchantCapabilities = .threeDSecure
636
+ paymentRequest.countryCode = data.countryCode
637
+ paymentRequest.currencyCode = data.currencyCode
638
+ paymentRequest.supportedNetworks = SkipcashReactnative.supportedNetworks
639
+
640
+ let paymentController = PKPaymentAuthorizationController(paymentRequest: paymentRequest)
641
+
642
+ paymentController.delegate = self
643
+
644
+ paymentController.present(completion: { (presented: Bool) in
645
+ if presented {
646
+ debugPrint("Presented payment controller")
647
+ } else {
648
+ debugPrint("Failed to present payment controller")
649
+ self.completionHandler(false)
650
+ }
651
+ })
652
+ }
653
+
654
+ @objc(initiatePaymentModel:)
655
+ func initiatePaymentModel(jsonString: String){
656
+ if let jsonData = jsonString.data(using: .utf8) {
657
+ do {
658
+ if let paymentDictionary = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] {
659
+ if let paymentData = PaymentData(data: paymentDictionary) {
660
+ self.getPaymentID(authorizationHeader: paymentData.authorizationHeader, data: paymentData, createPaymentApi: paymentData.createPaymentLinkEndPoint, returnURL: paymentData.returnUrl) { result in
661
+ if let result = result {
662
+ let paymentID = result["paymentID"] as? String ?? ""
663
+ let payUrl = result["payUrl"] as? String ?? ""
664
+ let transactionID = result["transactionId"] as? String ?? ""
665
+
666
+ if(paymentID.count == 0){
667
+ debugPrint("new payment request failed!")
668
+ return
669
+ }
670
+
671
+ self.showPaymentModal(url: payUrl, paymentTitle: paymentData.paymentModalTitle, returnURL: paymentData.returnUrl, paymentId: paymentID, transactionId: transactionID)
672
+
673
+ }
674
+ }
675
+ } else {
676
+ debugPrint("Unable to convert jsonData(cross-platform passed data) to paymentDictionary")
677
+ let responseData: [String: Any] = [
678
+ "transactionId": self.transactionID,
679
+ "paymentId": self.paymentID,
680
+ "isSuccess": false,
681
+ "token": "",
682
+ "returnCode": 400,
683
+ "errorMessage": ""
684
+ ]
685
+
686
+ self.sendEvent(withName: "responseScPaymentData", body: responseData)
687
+ return
688
+ }
689
+
690
+ } else {
691
+ debugPrint("Error: Unable to convert JSON string to data")
692
+ let responseData: [String: Any] = [
693
+ "transactionId": self.transactionID,
694
+ "paymentId": self.paymentID,
695
+ "isSuccess": false,
696
+ "token": "",
697
+ "returnCode": 400,
698
+ "errorMessage": ""
699
+ ]
700
+
701
+
702
+ self.sendEvent(withName: "responseScPaymentData", body: responseData)
703
+ return
704
+ }
705
+ } catch {
706
+ debugPrint("Error: \(error)")
707
+ let responseData: [String: Any] = [
708
+ "transactionId": self.transactionID,
709
+ "paymentId": self.paymentID,
710
+ "isSuccess": false,
711
+ "token": "",
712
+ "returnCode": 400,
713
+ "errorMessage": "\(error.localizedDescription)"
714
+ ]
715
+
716
+ self.sendEvent(withName: "responseScPaymentData", body: responseData)
717
+ return
718
+ }
719
+ }
720
+ }
721
+ }
722
+
723
+
724
+ extension SkipcashReactnative: PKPaymentAuthorizationControllerDelegate {
725
+
726
+ public func paymentAuthorizationController(_ controller: PKPaymentAuthorizationController, didAuthorizePayment payment: PKPayment, handler completion: @escaping (PKPaymentAuthorizationResult) -> Void) {
727
+
728
+ let errors = [Error]()
729
+ let status = PKPaymentAuthorizationStatus.success
730
+
731
+ var token = ""
732
+
733
+ do {
734
+ if let jsonResponse = try JSONSerialization.jsonObject(with: payment.token.paymentData, options: []) as? [String: Any] {
735
+ token = String(decoding: payment.token.paymentData, as: UTF8.self)
736
+ } else {
737
+ debugPrint("Failed to get applepay token")
738
+
739
+ let errors = [Error]()
740
+ let status = PKPaymentAuthorizationStatus.failure
741
+
742
+ self.paymentStatus = status
743
+ completion(PKPaymentAuthorizationResult(status: status, errors: errors))
744
+ return
745
+ }
746
+ } catch {
747
+ debugPrint("error converting payment token")
748
+
749
+ let errors = [Error]()
750
+ let status = PKPaymentAuthorizationStatus.failure
751
+
752
+ self.paymentStatus = status
753
+ completion(PKPaymentAuthorizationResult(status: status, errors: errors))
754
+ return
755
+ }
756
+
757
+ //
758
+ let vc = SetupVC()
759
+
760
+ vc.modalPresentationStyle = .overCurrentContext
761
+ vc.delegate = self
762
+ vc.transactionID = self.transactionID
763
+ vc.paymentID = self.paymentID
764
+ vc.completion = completion
765
+ vc.paymentToken = token
766
+
767
+ if let topViewController = UIApplication.shared.keyWindow?.rootViewController?.topMostViewController() {
768
+ topViewController.modalPresentationStyle = .overCurrentContext
769
+ topViewController.present(vc, animated: true, completion: nil)
770
+ }
771
+ }
772
+
773
+ public func paymentAuthorizationControllerDidFinish(_ controller: PKPaymentAuthorizationController) {
774
+ controller.dismiss {
775
+ DispatchQueue.main.async {
776
+ if self.paymentStatus == .success {
777
+ self.completionHandler!(true)
778
+ } else {
779
+ self.completionHandler!(false)
780
+ }
781
+ }
782
+ }
783
+ }
784
+ }
785
+
786
+
787
+ extension SkipcashReactnative: WKNavigationDelegate {
788
+
789
+ @objc func showPaymentModal(url: String, paymentTitle: String, returnURL: String, paymentId: String, transactionId: String){
790
+ DispatchQueue.main.async {
791
+ self.returnURL = returnURL
792
+ self.transactionID = transactionId
793
+ self.paymentID = paymentId
794
+ let webViewController = UIViewController()
795
+ let webConfiguration = WKWebViewConfiguration()
796
+
797
+ if let topViewController = UIApplication.shared.keyWindow?.rootViewController?.topMostViewController() {
798
+
799
+ self.webView = WKWebView(frame: webViewController.view.bounds, configuration: webConfiguration)
800
+ self.webView.navigationDelegate = self
801
+
802
+ self.clearWebViewCache()
803
+
804
+ self.setupActivityIndicator()
805
+
806
+ if let myURL = URL(string: url) {
807
+ let myRequest = URLRequest(url: myURL)
808
+ self.webView.load(myRequest)
809
+ } else {
810
+ debugPrint("Invalid URL: \(url)")
811
+ }
812
+
813
+
814
+
815
+ webViewController.view.addSubview(self.webView)
816
+ self.webView.frame = webViewController.view.bounds
817
+ self.webView.scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 50,right:0)
818
+
819
+
820
+ webViewController.title = paymentTitle
821
+
822
+ let backButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action:#selector(self.goBackBtn))
823
+
824
+ webViewController.navigationItem.setLeftBarButton(backButton, animated: true)
825
+
826
+ if let navigationController = topViewController.navigationController {
827
+ navigationController.modalPresentationStyle = .pageSheet
828
+ navigationController.isModalInPresentation = true
829
+ navigationController.pushViewController(webViewController, animated: true)
830
+ } else {
831
+ let navigationController = UINavigationController(rootViewController: webViewController)
832
+ navigationController.modalPresentationStyle = .pageSheet
833
+ navigationController.isModalInPresentation = true
834
+ topViewController.present(navigationController, animated: true, completion: nil)
835
+ }
836
+
837
+ } else {
838
+ debugPrint("Unable to find top view controller")
839
+ }
840
+ }
841
+ }
842
+
843
+ private func configureWebView() {
844
+ // Inject viewport meta tag for scaling and zooming
845
+ let viewportMetaTag = """
846
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
847
+ """
848
+ let injectViewportJS = "document.head.insertAdjacentHTML('beforeend', '\(viewportMetaTag)');"
849
+ webView.evaluateJavaScript(injectViewportJS, completionHandler: nil)
850
+ }
851
+
852
+ private func clearWebViewCache() {
853
+ let dataStore = webView.configuration.websiteDataStore
854
+ dataStore.httpCookieStore.getAllCookies { cookies in
855
+ cookies.forEach { cookie in
856
+ dataStore.httpCookieStore.delete(cookie)
857
+ }
858
+ }
859
+
860
+ dataStore.fetchDataRecords(ofTypes: WKWebsiteDataStore.allWebsiteDataTypes()) { records in
861
+ for record in records {
862
+ dataStore.removeData(ofTypes: record.dataTypes, for: [record]) {
863
+ debugPrint("Cleared data for \(record)")
864
+ }
865
+ }
866
+ }
867
+ }
868
+
869
+ private func setupActivityIndicator() {
870
+ self.activityIndicator = UIActivityIndicatorView(style: .large)
871
+ self.activityIndicator.translatesAutoresizingMaskIntoConstraints = false
872
+ self.activityIndicator.hidesWhenStopped = true
873
+ self.webView.addSubview(self.activityIndicator)
874
+
875
+ NSLayoutConstraint.activate([
876
+ self.activityIndicator.centerXAnchor.constraint(equalTo: self.webView.centerXAnchor),
877
+ self.activityIndicator.centerYAnchor.constraint(equalTo: self.webView.centerYAnchor)
878
+ ])
879
+ }
880
+
881
+ public func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
882
+ self.activityIndicator.startAnimating()
883
+ self.configureWebView()
884
+ }
885
+
886
+ public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
887
+ DispatchQueue.main.async {
888
+ self.activityIndicator.stopAnimating()
889
+ // scroll to web view bottom to show the payment buttons clearly
890
+ let bottomOffset = CGPoint(x: 0, y: webView.scrollView.contentSize.height - webView.scrollView.bounds.height)
891
+ if bottomOffset.y > 0 {
892
+ webView.scrollView.setContentOffset(bottomOffset, animated: true)
893
+ }
894
+ self.configureWebView()
895
+ }
896
+ }
897
+
898
+ public func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
899
+ self.configureWebView()
900
+ if let currentURL = webView.url {
901
+ if currentURL.absoluteString.range(of: self.returnURL!, options: .caseInsensitive) != nil {
902
+ if let queryItems = URLComponents(url: currentURL, resolvingAgainstBaseURL: false)?.queryItems {
903
+
904
+ var queryParameters = [String: String]()
905
+ for item in queryItems {
906
+ queryParameters[item.name] = item.value
907
+ }
908
+ goBack(data: queryParameters)
909
+ return
910
+ }
911
+
912
+ var queryParameters = [String: String]()
913
+
914
+ queryParameters["id"] = self.paymentID
915
+ queryParameters["statusId"] = "4"
916
+ queryParameters["status"] = "Failed"
917
+ queryParameters["transId"] = self.transactionID
918
+ queryParameters["custom1"] = ""
919
+
920
+ goBack(data: queryParameters)
921
+ return
922
+ }
923
+ }
924
+ }
925
+
926
+
927
+ @objc func goBackBtn(){ // cancel the payment by user
928
+ DispatchQueue.main.async {
929
+
930
+
931
+ if let topViewController = UIApplication.shared.keyWindow?.rootViewController?.topMostViewController() {
932
+
933
+ var data = [String: String]()
934
+
935
+ data["id"] = self.paymentID
936
+ data["statusId"] = "4"
937
+ data["status"] = "Failed"
938
+ data["transId"] = self.transactionID
939
+ data["custom1"] = ""
940
+
941
+ if let navigationController = topViewController.navigationController {
942
+ } else {
943
+ }
944
+
945
+ self.navigationController?.popViewController(animated: true)
946
+ topViewController.dismiss(animated: true) {
947
+ }
948
+
949
+ self.sendEvent(withName: "responseScPaymentData", body: data)
950
+
951
+
952
+ } else {
953
+ debugPrint("Unable to find top view controller")
954
+ }
955
+ }
956
+ }
957
+
958
+ @objc func goBack(data: [String: String]) {
959
+ DispatchQueue.main.async {
960
+ if let topViewController = UIApplication.shared.keyWindow?.rootViewController?.topMostViewController() {
961
+ if let navigationController = topViewController.navigationController {
962
+ } else {
963
+ }
964
+ self.navigationController?.popViewController(animated: true)
965
+ topViewController.dismiss(animated: true) {
966
+
967
+ }
968
+ self.sendEvent(withName: "responseScPaymentData", body: data)
969
+ } else {
970
+ debugPrint("Unable to find top view controller")
971
+ }
972
+ }
973
+ }
974
+
975
+ }
976
+
977
+ extension UIViewController {
978
+ func topMostViewController() -> UIViewController {
979
+ if let presentedViewController = presentedViewController {
980
+ presentedViewController.modalPresentationStyle = .overCurrentContext
981
+ return presentedViewController.topMostViewController()
982
+ }
983
+ if let navigationController = self as? UINavigationController {
984
+ navigationController.modalPresentationStyle = .overCurrentContext
985
+ return navigationController.visibleViewController?.topMostViewController() ?? navigationController
986
+ }
987
+ if let tabBarController = self as? UITabBarController {
988
+ tabBarController.modalPresentationStyle = .overCurrentContext
989
+ return tabBarController.selectedViewController?.topMostViewController() ?? tabBarController
990
+ }
991
+ self.modalPresentationStyle = .overCurrentContext
992
+ return self
993
+ }
994
+ }