Skip to content

Swe 12 enable payment suite #13

Merged
merged 14 commits into from Apr 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -201,31 +201,60 @@
<objects>
<controller id="t8d-XB-ngB" customClass="PaymentDetailInterfaceController" customModule="SynchronyFinancial_WatchKit_App" customModuleProvider="target">
<items>
<label width="1" alignment="center" text="Pay From:" textAlignment="center" id="xE5-Zl-U7d"/>
<button width="1" height="40" alignment="left" title="Synchrony (1234)" id="SZD-0B-PrF" userLabel="detailButton">
<fontDescription key="font" type="system" pointSize="15"/>
</button>
<separator alignment="left" verticalAlignment="center" id="et0-Sq-Qxm">
<color key="color" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</separator>
<label width="1" alignment="center" verticalAlignment="bottom" text="Confirm Payment" textAlignment="center" id="mT0-3D-JGP"/>
<button width="1" height="49" alignment="left" verticalAlignment="bottom" title="Button" id="FgU-iZ-2XU" userLabel="paymentButton">
<color key="titleColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" red="0.16078431369999999" green="0.6705882353" blue="0.8862745098" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<action selector="paymentAction" destination="t8d-XB-ngB" id="Zjv-zI-wCI"/>
</connections>
</button>
<imageView alignment="left" hidden="YES" id="qfi-KI-QXK"/>
<label width="1" alignment="center" hidden="YES" text="Processing ..." textAlignment="center" id="kgd-iG-og7"/>
<group width="1" height="1" alignment="left" layout="vertical" id="9VF-fb-CV3">
<items>
<label width="1" alignment="center" text="Pay From:" textAlignment="center" id="xE5-Zl-U7d"/>
<button width="1" height="40" alignment="left" title="Synchrony (1234)" id="SZD-0B-PrF" userLabel="detailButton">
<fontDescription key="font" type="system" pointSize="15"/>
</button>
<separator alignment="left" verticalAlignment="center" id="et0-Sq-Qxm">
<color key="color" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</separator>
<label width="1" alignment="center" verticalAlignment="bottom" text="Confirm Payment" textAlignment="center" id="mT0-3D-JGP"/>
<button width="1" height="49" alignment="left" verticalAlignment="bottom" title="Button" id="FgU-iZ-2XU" userLabel="paymentButton">
<color key="titleColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" red="0.16078431369999999" green="0.6705882353" blue="0.8862745098" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<action selector="paymentAction" destination="t8d-XB-ngB" id="Zjv-zI-wCI"/>
</connections>
</button>
</items>
</group>
</items>
<connections>
<outlet property="activityIndicator" destination="qfi-KI-QXK" id="wVj-x3-NzV"/>
<outlet property="activityIndicatorLabel" destination="kgd-iG-og7" id="csx-e2-1eZ"/>
<outlet property="amount" destination="mT0-3D-JGP" id="rff-wG-B82"/>
<outlet property="contentGroup" destination="9VF-fb-CV3" id="vfa-YT-ipt"/>
<outlet property="detailButton" destination="SZD-0B-PrF" id="xEw-gE-yIP"/>
<outlet property="paymentButton" destination="FgU-iZ-2XU" id="yZe-dZ-nBV"/>
</connections>
</controller>
</objects>
<point key="canvasLocation" x="1085" y="186"/>
</scene>
<!--PaymentResult-->
<scene sceneID="mFc-oM-oG1">
<objects>
<controller identifier="PaymentResult" id="mpK-7H-pjK" customClass="PaymentResultInterfaceController" customModule="SynchronyFinancial_WatchKit_App" customModuleProvider="target">
<items>
<label width="1" alignment="center" text="Payment Result:" textAlignment="center" id="gg1-Wq-NeI"/>
<separator width="1" alignment="center" id="Y1o-3Q-hjG"/>
<label width="1" alignment="center" text="Confirmation Number:" textAlignment="center" minimumScaleFactor="0.69999999999999996" id="qAX-bM-U5t"/>
<label width="1" alignment="center" text="Label" textAlignment="center" minimumScaleFactor="0.5" id="cF3-r2-BMf"/>
<label width="1" alignment="center" verticalAlignment="center" text="Payment ID:" textAlignment="center" id="7Cr-Kh-rJ2"/>
<label width="1" alignment="center" verticalAlignment="center" text="Label" textAlignment="center" id="wSp-wZ-Ksf"/>
</items>
<connections>
<outlet property="confirmationNumberLabel" destination="cF3-r2-BMf" id="FBR-K8-kdQ"/>
<outlet property="paymentIDLabel" destination="wSp-wZ-Ksf" id="7MN-ZT-Jxw"/>
</connections>
</controller>
</objects>
<point key="canvasLocation" x="1408" y="185"/>
</scene>
<!--Transactions-->
<scene sceneID="yXO-yR-k6b">
<objects>
Expand Down
Expand Up @@ -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")
Expand All @@ -43,17 +43,14 @@ 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,
let last4 = $0["last4_acct_number"].string,
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,
Expand Down Expand Up @@ -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,
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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):
Expand All @@ -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)")
}
}
}
}
Expand Up @@ -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)
}
}
}
Expand Up @@ -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
Expand All @@ -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")
}
}
}

Expand Down
@@ -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()
}

}
Expand Up @@ -10,7 +10,6 @@ import Foundation
import WatchKit

class TransactionCell: NSObject {

@IBOutlet weak var transactionLabel: WKInterfaceLabel!
@IBOutlet weak var valueLabel: WKInterfaceLabel!
}