diff --git a/SynchronyFinancial/SynchronyFinancial WatchKit App/Base.lproj/Interface.storyboard b/SynchronyFinancial/SynchronyFinancial WatchKit App/Base.lproj/Interface.storyboard
index 037b541..c2bdbb3 100644
--- a/SynchronyFinancial/SynchronyFinancial WatchKit App/Base.lproj/Interface.storyboard
+++ b/SynchronyFinancial/SynchronyFinancial WatchKit App/Base.lproj/Interface.storyboard
@@ -201,24 +201,33 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -226,6 +235,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SynchronyFinancial/SynchronyFinancial WatchKit Extension/FetchData.swift b/SynchronyFinancial/SynchronyFinancial WatchKit Extension/FetchData.swift
index 79efb3e..5426ea7 100644
--- a/SynchronyFinancial/SynchronyFinancial WatchKit Extension/FetchData.swift
+++ b/SynchronyFinancial/SynchronyFinancial WatchKit Extension/FetchData.swift
@@ -20,7 +20,7 @@ class FetchData {
completion(false, NSError())
return
}
-
+
if let token = dict["access_token"]?.string {
// now we should save our token somewhere safe (UserDefaults)
UserDefaults.standard.set(token, forKey: "access_token")
@@ -43,9 +43,6 @@ class FetchData {
guard dict["status"]?.dictionaryValue["response_code"]?.string == "0" else { return }
//print(json)
- let formatter = DateFormatter()
- formatter.locale = Locale.current
- formatter.dateFormat = "yyyyMMdd"
if let accounts = dict["account_number_list"]?.arrayValue {
accounts.forEach {
if let accountAlias = $0.dictionaryValue["account_alias"]?.string,
@@ -53,7 +50,7 @@ class FetchData {
let creditLimitString = $0.dictionaryValue["credit_limit"]?.string,
let creditLimit = Double(creditLimitString),
let payDueDateString = $0.dictionaryValue["next_payment_due_date"]?.string,
- let paymentDueDate = formatter.date(from: payDueDateString),
+ let paymentDueDate = Defaults.careCreditDateFormatter.date(from: payDueDateString),
let curBalString = $0.dictionaryValue["current_balance"]?.string,
let currentBalance = Double(curBalString),
let availCreditString = $0.dictionaryValue["available_credit"]?.string,
@@ -81,20 +78,17 @@ class FetchData {
var header = Defaults.headerForTransaction
header["account_alias"] = accountAlias
var transactions: [Transaction] = []
- let formatter = DateFormatter()
- formatter.locale = Locale.current
- formatter.dateFormat = "yyyyMMdd"
Alamofire.request(Defaults.TRANS_HISTORY_URL, method: .post, parameters: header, encoding: JSONEncoding.default, headers: Defaults.authHeader).responseJSON { payload in
switch payload.result {
case .success(let value):
let dict = JSON(value).dictionaryValue
guard dict["status"]?.dictionaryValue["response_code"]?.string == "0" else { return }
-
+
//let's first parse the pending transactions
dict["pending_transaction_list"]?.arrayValue.forEach {
if let desc = $0["description"].string,
- let date = formatter.date(from: $0["transaction_date"].stringValue),
+ let date = Defaults.careCreditDateFormatter.date(from: $0["transaction_date"].stringValue),
let amountString = $0["transaction_amount"].string,
let amount = Double(amountString),
let confirmationNum = $0["payment_confirmation_number"].string,
@@ -103,15 +97,15 @@ class FetchData {
let modifiable = $0["is_payment_modifiable"].string {
let type: TransactionType = $0["payment_amount_type"].stringValue == "" ? .purchase : .reimbursement
let isModifiable: Bool = modifiable == "Y" ? true : false
-
+
transactions.append(Transaction(type: type, amount: amount, merchantID: desc, date: date, confirmationNum: confirmationNum, paymentId: paymentId, isPending: true, isModifiable: isModifiable))
}
}
-
+
// let's parse just processed transactions now
dict["processed_transaction_list"]?.arrayValue.forEach {
if let desc = $0["description"].string,
- let date = formatter.date(from: $0["transaction_date"].stringValue),
+ let date = Defaults.careCreditDateFormatter.date(from: $0["transaction_date"].stringValue),
let amountString = $0["transaction_amount"].string,
let amount = Double(amountString) {
let type: TransactionType = $0["payment_amount_type"].stringValue == "" ? .purchase : .reimbursement
@@ -125,7 +119,7 @@ class FetchData {
}
}
}
-
+
static func getBankInfo(completion: @escaping ([BankAcct], Error?) -> Void) {
var bankIds: [BankAcct] = []
Alamofire.request(Defaults.FETCH_BANKS_URL, method: .post, parameters: Defaults.headerForMulti, encoding: JSONEncoding.default, headers: Defaults.authHeader).responseJSON { payload in
@@ -151,14 +145,13 @@ class FetchData {
}
}
}
-
- static func cancelPayment(accountAlias: String, confirmationNum: String, paymentId: Int, completion: @escaping (String, Error?) -> Void){
-
+
+ static func cancelPayment(accountAlias: String, confirmationNum: String, paymentId: Int, completion: @escaping (String, Error?) -> Void) {
var paymentHeader = Defaults.headerForCancelPmt
paymentHeader["account_alias"] = accountAlias
paymentHeader["payment_confirmation_number"] = confirmationNum
paymentHeader["payment_id"] = paymentId
-
+
Alamofire.request(Defaults.CANCEL_PAYMENT_URL, method: .post, parameters: paymentHeader, encoding: JSONEncoding.default, headers: Defaults.authHeader).responseJSON { payload in
switch payload.result {
case .success(let value):
@@ -172,4 +165,27 @@ class FetchData {
}
}
}
+
+ static func submitPayment(for alias: String, type: PaymentType, amount: Double, bankID: String, completion: @escaping (String, String, Error?) -> Void) {
+ var paymentHeader = Defaults.headerForPmt
+ paymentHeader["account_alias"] = alias
+ paymentHeader["bank_account_id"] = bankID
+ paymentHeader["payment_amount_type"] = type.rawValue
+ paymentHeader["payment_amount"] = amount
+ paymentHeader["scheduled_payment_post_date"] = Defaults.careCreditDateFormatter.string(from: Date())
+
+ Alamofire.request(Defaults.MAKE_PAYMENT_URL, method: .post, parameters: paymentHeader, encoding: JSONEncoding.default, headers: Defaults.authHeader).responseJSON { payload in
+ switch payload.result {
+ case .success(let value):
+ let dict = JSON(value).dictionaryValue
+ guard dict["status"]?.dictionaryValue["response_code"]?.string == "0" else { return }
+ if let paymentConfirmationNum = dict["payment_confirmation_number"]?.stringValue,
+ let paymentID = dict["payment_id"]?.stringValue {
+ completion(paymentConfirmationNum, paymentID, nil)
+ }
+ case .failure(let error):
+ NSLog("Error: \(error.localizedDescription)")
+ }
+ }
+ }
}
diff --git a/SynchronyFinancial/SynchronyFinancial WatchKit Extension/PayBillInterfaceController.swift b/SynchronyFinancial/SynchronyFinancial WatchKit Extension/PayBillInterfaceController.swift
index 3593120..5facc30 100644
--- a/SynchronyFinancial/SynchronyFinancial WatchKit Extension/PayBillInterfaceController.swift
+++ b/SynchronyFinancial/SynchronyFinancial WatchKit Extension/PayBillInterfaceController.swift
@@ -60,6 +60,9 @@ class PayBillInterfaceController: WKInterfaceController {
let minimumFormatted = String(format: "$%.2f", valid.minPayDue)
minimumLabel.setText("Minimum Payment:\n\(minimumFormatted)")
payMinimumButton.setTitle("Pay \(minimumFormatted)")
+
+ payMinimumButton.setEnabled(valid.minPayDue > 0.0)
+ payBalanceButton.setEnabled(valid.curBalance > 0.0)
}
}
}
diff --git a/SynchronyFinancial/SynchronyFinancial WatchKit Extension/PaymentDetailInterfaceController.swift b/SynchronyFinancial/SynchronyFinancial WatchKit Extension/PaymentDetailInterfaceController.swift
index 30ed7b8..99ab3b1 100644
--- a/SynchronyFinancial/SynchronyFinancial WatchKit Extension/PaymentDetailInterfaceController.swift
+++ b/SynchronyFinancial/SynchronyFinancial WatchKit Extension/PaymentDetailInterfaceController.swift
@@ -12,18 +12,25 @@ import Foundation
class PaymentDetailInterfaceController: WKInterfaceController {
var selectedAccount: Account?
var dictForAcct: [String: Account] = [:]
+ var dictForPayment: [String: String] = [:]
var paymentButtonArmed: Bool = false
var paymentAmount: Double = 0.0
+ @IBOutlet weak var contentGroup: WKInterfaceGroup!
+ @IBOutlet weak var activityIndicator: WKInterfaceImage!
+ @IBOutlet weak var activityIndicatorLabel: WKInterfaceLabel!
@IBOutlet weak var detailButton: WKInterfaceButton!
@IBOutlet weak var amount: WKInterfaceLabel!
@IBOutlet weak var paymentButton: WKInterfaceButton!
override func awake(withContext context: Any?) {
super.awake(withContext: context)
- guard let data = context as? [String: Any], let acct = data["acct"] as? Account, let amount = data["payment_amount"] as? Double else {
- NSLog("Error getting account object and payment amount")
- return
+
+ guard let data = context as? [String: Any],
+ let acct = data["acct"] as? Account,
+ let amount = data["payment_amount"] as? Double else {
+ NSLog("Error getting account object and payment amount")
+ return
}
self.paymentAmount = amount
@@ -33,15 +40,38 @@ class PaymentDetailInterfaceController: WKInterfaceController {
@IBAction func paymentAction() {
if paymentButtonArmed {
- popToRootController()
- } else {
-// animate(withDuration: 0.75) {
-// self.paymentButton.setBackgroundColor(UIColor.init(red: 141, green: 241, blue: 48, alpha: 1.0))
-// }
+ guard let alias = selectedAccount?.accountAlias else { return }
+ let cancel = WKAlertAction(title: "Cancel", style: .cancel, handler: {})
+ let submit = WKAlertAction(title: "Pay Now", style: .default, handler: {
+ self.contentGroup.setHidden(true)
+ self.activityIndicator.configureForActivityIndicator()
+ self.activityIndicatorLabel.setHidden(false)
+ self.activityIndicatorLabel.setVerticalAlignment(.center)
+ let type: PaymentType = self.paymentAmount == self.selectedAccount?.curBalance ? .currentBal : .minimumDue
- paymentButton.setBackgroundColor(#colorLiteral(red: 0.6092301607, green: 0.9366738796, blue: 0.2432599962, alpha: 1))
- paymentButtonArmed = true
- paymentButton.setTitle("Pay Now")
+ // process this payment using default bank account (9999) until ability to change account is implemented
+ FetchData.submitPayment(for: alias, type: type, amount: self.paymentAmount, bankID: "9999") { confirmationNum, paymentID, error in
+ guard error == nil else {
+ let dismiss = WKAlertAction(title: "Dismiss", style: .cancel, handler: {})
+ self.presentAlert(withTitle: "Error", message: "We were unable to process your payment at this time. Please try again later.", preferredStyle: .alert, actions: [dismiss])
+ return
+ }
+ self.dictForPayment.updateValue(confirmationNum, forKey: "payment_confirmation_number")
+ self.dictForPayment.updateValue(paymentID, forKey: "payment_id")
+ self.activityIndicator.stopAnimatingAsIndicator()
+ self.activityIndicatorLabel.setHidden(true)
+ self.contentGroup.setHidden(false)
+ self.presentController(withName: "PaymentResult", context: self.dictForPayment)
+ self.popToRootController()
+ }
+ })
+ presentAlert(withTitle: "Disclaimer", message: "By tapping \"Pay Now\", you are authorizing Synchrony Bank to process a one time payment in the amount of \(String(format: "$%.2f", paymentAmount)).", preferredStyle: .actionSheet, actions: [cancel, submit])
+ } else {
+ animate(withDuration: 0.5) {
+ self.paymentButton.setBackgroundColor(#colorLiteral(red: 0.6092301607, green: 0.9366738796, blue: 0.2432599962, alpha: 1))
+ self.paymentButtonArmed = true
+ self.paymentButton.setTitle("Pay Now")
+ }
}
}
diff --git a/SynchronyFinancial/SynchronyFinancial WatchKit Extension/PaymentResultInterfaceController.swift b/SynchronyFinancial/SynchronyFinancial WatchKit Extension/PaymentResultInterfaceController.swift
new file mode 100644
index 0000000..3208d1c
--- /dev/null
+++ b/SynchronyFinancial/SynchronyFinancial WatchKit Extension/PaymentResultInterfaceController.swift
@@ -0,0 +1,38 @@
+//
+// PaymentResultInterfaceController.swift
+// SynchronyFinancial WatchKit Extension
+//
+// Created by Alan Maynard on 3/29/19.
+// Copyright © 2019 Alan Maynard. All rights reserved.
+//
+
+import WatchKit
+import Foundation
+
+class PaymentResultInterfaceController: WKInterfaceController {
+ @IBOutlet weak var confirmationNumberLabel: WKInterfaceLabel!
+ @IBOutlet weak var paymentIDLabel: WKInterfaceLabel!
+
+ override func awake(withContext context: Any?) {
+ super.awake(withContext: context)
+
+ if let data = context as? [String: String],
+ let paymentConfirmation = data["payment_confirmation_number"],
+ let paymentID = data["payment_id"] {
+ confirmationNumberLabel.setText(paymentConfirmation)
+ paymentIDLabel.setText(paymentID)
+ setTitle("Done")
+ }
+ }
+
+ override func willActivate() {
+ // This method is called when watch view controller is about to be visible to user
+ super.willActivate()
+ }
+
+ override func didDeactivate() {
+ // This method is called when watch view controller is no longer visible
+ super.didDeactivate()
+ }
+
+}
diff --git a/SynchronyFinancial/SynchronyFinancial WatchKit Extension/TransactionCell.swift b/SynchronyFinancial/SynchronyFinancial WatchKit Extension/TransactionCell.swift
index e9d39bd..8825369 100644
--- a/SynchronyFinancial/SynchronyFinancial WatchKit Extension/TransactionCell.swift
+++ b/SynchronyFinancial/SynchronyFinancial WatchKit Extension/TransactionCell.swift
@@ -10,7 +10,6 @@ import Foundation
import WatchKit
class TransactionCell: NSObject {
-
@IBOutlet weak var transactionLabel: WKInterfaceLabel!
@IBOutlet weak var valueLabel: WKInterfaceLabel!
}
diff --git a/SynchronyFinancial/SynchronyFinancial.xcodeproj/project.pbxproj b/SynchronyFinancial/SynchronyFinancial.xcodeproj/project.pbxproj
index 5f786f7..ceb6fe6 100644
--- a/SynchronyFinancial/SynchronyFinancial.xcodeproj/project.pbxproj
+++ b/SynchronyFinancial/SynchronyFinancial.xcodeproj/project.pbxproj
@@ -11,7 +11,6 @@
11E6ADB92253FA050009922E /* BankAcct.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11E6ADB82253FA050009922E /* BankAcct.swift */; };
11E6ADBA225401DB0009922E /* BankAcct.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11E6ADB82253FA050009922E /* BankAcct.swift */; };
281283568A34D3C5D9C7B383 /* libPods-SynchronyFinancial WatchKit Extension.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CAA6D46F907ADAABF49FD409 /* libPods-SynchronyFinancial WatchKit Extension.a */; };
- 481864A8224802BB0059CF7A /* PaymentDetailInterfaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 481864A7224802BB0059CF7A /* PaymentDetailInterfaceController.swift */; };
481864A9224802BB0059CF7A /* PaymentDetailInterfaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 481864A7224802BB0059CF7A /* PaymentDetailInterfaceController.swift */; };
48DA0058221D12E70081A500 /* AccountCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48DA0057221D12E70081A500 /* AccountCell.swift */; };
48F243072214C98600B9C894 /* AccountTableInterfaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48F243062214C98600B9C894 /* AccountTableInterfaceController.swift */; };
@@ -29,6 +28,7 @@
678C38842230950100FEAAF6 /* AccountDetailsInterfaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678C38832230950100FEAAF6 /* AccountDetailsInterfaceController.swift */; };
678C3885223098C400FEAAF6 /* Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48F2430B2214CBF700B9C894 /* Account.swift */; };
678C388622309F7D00FEAAF6 /* AccountCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48DA0057221D12E70081A500 /* AccountCell.swift */; };
+ 678C62BA224ECDFD0007AD53 /* PaymentResultInterfaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678C62B9224ECDFD0007AD53 /* PaymentResultInterfaceController.swift */; };
67BAC269219E254700713FEF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67BAC268219E254700713FEF /* AppDelegate.swift */; };
67BAC26E219E254700713FEF /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 67BAC26C219E254700713FEF /* Main.storyboard */; };
67BAC270219E254800713FEF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 67BAC26F219E254800713FEF /* Assets.xcassets */; };
@@ -104,6 +104,7 @@
676392B322429DC800740A8C /* TransactionsInterfaceController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionsInterfaceController.swift; sourceTree = ""; };
676392B52242A3F800740A8C /* TransactionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionCell.swift; sourceTree = ""; };
678C38832230950100FEAAF6 /* AccountDetailsInterfaceController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountDetailsInterfaceController.swift; sourceTree = ""; };
+ 678C62B9224ECDFD0007AD53 /* PaymentResultInterfaceController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentResultInterfaceController.swift; sourceTree = ""; };
67BAC265219E254700713FEF /* SynchronyFinancial.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SynchronyFinancial.app; sourceTree = BUILT_PRODUCTS_DIR; };
67BAC268219E254700713FEF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
67BAC26D219E254700713FEF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
@@ -204,6 +205,7 @@
67BAC28A219E254900713FEF /* SynchronyFinancial WatchKit Extension */ = {
isa = PBXGroup;
children = (
+ 678C62B9224ECDFD0007AD53 /* PaymentResultInterfaceController.swift */,
674BD1522239A39D0076AFD6 /* PayBillInterfaceController.swift */,
481864A7224802BB0059CF7A /* PaymentDetailInterfaceController.swift */,
676392B322429DC800740A8C /* TransactionsInterfaceController.swift */,
@@ -486,6 +488,7 @@
678C3885223098C400FEAAF6 /* Account.swift in Sources */,
67BAC28E219E254900713FEF /* ExtensionDelegate.swift in Sources */,
674BD1542239A39D0076AFD6 /* PayBillInterfaceController.swift in Sources */,
+ 678C62BA224ECDFD0007AD53 /* PaymentResultInterfaceController.swift in Sources */,
678C38842230950100FEAAF6 /* AccountDetailsInterfaceController.swift in Sources */,
673F396E21A644570051469E /* MainMenuInterfaceController.swift in Sources */,
67E17B87223812C2008871FE /* Defaults.swift in Sources */,
diff --git a/SynchronyFinancial/SynchronyFinancial/Defaults.swift b/SynchronyFinancial/SynchronyFinancial/Defaults.swift
index 375d8a3..55958a8 100644
--- a/SynchronyFinancial/SynchronyFinancial/Defaults.swift
+++ b/SynchronyFinancial/SynchronyFinancial/Defaults.swift
@@ -22,6 +22,13 @@ final class Defaults {
static let token = UserDefaults.standard.string(forKey: "access_token") ?? ""
static let authHeader = ["Authorization": "Bearer \(token)"]
+ static let careCreditDateFormatter: DateFormatter = {
+ var formatter = DateFormatter()
+ formatter.dateFormat = "yyyyMMdd"
+ formatter.locale = Locale.current
+ return formatter
+ }()
+
static var headerForLogin: [String: Any] = {
return ["client_id": "carecredit",
"client_secret": "",
@@ -47,14 +54,9 @@ final class Defaults {
var account_alias = ""
// Dates
- let formatter = DateFormatter()
- formatter.locale = Locale.current
- formatter.dateFormat = "yyyyMMdd"
-
// for now lets get transactions from the past 2 weeks
- let start_date = formatter.string(from: Calendar.current.date(byAdding: .day, value: -14, to: Date()) ?? Date())
- let end_date = formatter.string(from: Date())
-
+ let start_date = careCreditDateFormatter.string(from: Calendar.current.date(byAdding: .day, value: -14, to: Date()) ?? Date())
+ let end_date = careCreditDateFormatter.string(from: Date())
return ["account_alias": account_alias,
"begin_sequence": "1",
diff --git a/SynchronyFinancial/SynchronyFinancial/Transaction.swift b/SynchronyFinancial/SynchronyFinancial/Transaction.swift
index 71c58b0..e24c2b3 100644
--- a/SynchronyFinancial/SynchronyFinancial/Transaction.swift
+++ b/SynchronyFinancial/SynchronyFinancial/Transaction.swift
@@ -12,6 +12,11 @@ public enum TransactionType: Int {
case reimbursement = 1
}
+public enum PaymentType: String {
+ case currentBal = "CBL"
+ case minimumDue = "MIN"
+}
+
class Transaction: NSObject {
var type: TransactionType
var amount: Double