Skip to content

Commit

Permalink
3.7.0
Browse files Browse the repository at this point in the history
  • Loading branch information
henryx-stripe committed Jun 24, 2024
1 parent e0032a2 commit 1071ee6
Show file tree
Hide file tree
Showing 744 changed files with 422,481 additions and 82,253 deletions.
18 changes: 17 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,23 @@ If you are using CocoaPods, update your Podfile:
```
pod 'StripeTerminal', '~> 3.0'
```
# 3.6.0 2024-05-23

# 3.7.0 2024-06-24
* Built with Xcode 15.2, Swift version 5.9.
* Beta: Surcharging is now available in private beta.
* added a `surchargeNotice` parameter to [`SCPCollectConfiguration`](https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPCollectConfiguration.html) to display a surcharge notice on the payment collection screen.
* added a `SCPSurcharge` field to the [`SCPCardPresentParameters`](https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPCardPresentParameters.html) object.
* added a [`SCPConfirmConfiguration`](https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPConfirmConfiguration.html) class to allow per-transaction overrides for [`confirmPaymentIntent`](https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPTerminal.html#/c:objc(cs)SCPTerminal(im)confirmPaymentIntent:completion:).
* added an `amountSurcharge` parameter to [`SCPConfirmConfiguration`](https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPConfirmConfiguration.html) to surcharge when confirming a payment.
* If you are interested in joining this beta, please email stripe-terminal-betas@stripe.com.
* Beta: Added a `collectData` method to collect eligible magstripe data, such as gift cards.
* If you are interested in joining this beta, please email stripe-terminal-betas@stripe.com.
* Update: Added [`SCPSimulateReaderUpdateLowBatterySucceedConnect`](https://stripe.dev/stripe-terminal-ios/docs/Enums/SCPSimulateReaderUpdate.html#/c:@E@SCPSimulateReaderUpdate@SCPSimulateReaderUpdateLowBatterySucceedConnect) to simulate an error scenario where a required update fails on a mobile reader due to low battery, but the SDK still successfully connects to the reader.
* see [Simulated reader updates](https://docs.stripe.com/terminal/references/testing?terminal-sdk-platform=ios#simulated-reader-updates) for details.
* Update: if a mobile reader receives the [`SCPErrorReaderMissingEncryptionKeys`](https://stripe.dev/stripe-terminal-ios/docs/Enums/SCPError.html#/c:@E@SCPError@SCPErrorReaderMissingEncryptionKeys) error during payment collection, the SDK will disconnect from the reader. Note that auto reconnection will not work in this scenario. The error will automatically recover once the reader is reconnected.
* Fix: Fixed a crash that occurred when canceling [`collectPaymentMethod`](https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPTerminal.html#/c:objc(cs)SCPTerminal(im)collectPaymentMethod:completion:) after [`confirmPaymentIntent`](https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPTerminal.html#/c:objc(cs)SCPTerminal(im)confirmPaymentIntent:completion:) had already been called on the `PaymentIntent`.

# 3.6.0 2024-05-13
* Built with Xcode 15.2, Swift version 5.9.
* Update: Using [`SCPOfflineBehaviorRequireOnline`](https://stripe.dev/stripe-terminal-ios/docs/Enums/SCPOfflineBehavior.html#/c:@E@SCPOfflineBehavior@SCPOfflineBehaviorRequireOnline) will attempt online network calls regardless of the current network status. This may cause requests while the network is offline to take longer as requests will always be attempted online first.
* Update: Tapping or inserting an unsupported card will now report `SCPReaderDisplayMessageTryAnotherCard` instead of `SCPReaderDisplayMessageTryAnotherReadMethod`.
Expand Down
80 changes: 44 additions & 36 deletions Example/Example.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

66 changes: 66 additions & 0 deletions Example/Example/CollectDataViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//
// CollectDataViewController.swift
// Example
//
// Created by Mindy Lou on 5/9/24.
// Copyright © 2024 Stripe. All rights reserved.
//

import UIKit
import Static
import StripeTerminal

class CollectDataViewController: EventDisplayingViewController {
override var cancelLogMethod: LogEvent.Method {
return .cancelCollectData
}

private let collectDataConfig: CollectDataConfiguration

init(collectDataConfiguration: CollectDataConfiguration) {
self.collectDataConfig = collectDataConfiguration
super.init()
}

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override func viewDidLoad() {
super.viewDidLoad()

collectData(config: collectDataConfig)
}

private func collectData(config: CollectDataConfiguration) {
var collectDataEvent = LogEvent(method: .collectData)
events.append(collectDataEvent)
cancelable = Terminal.shared.collectData(config, completion: { [weak self] collectedData, error in
if let error = error {
collectDataEvent.result = .errored
collectDataEvent.object = .error(error as NSError)
} else if let data = collectedData {
collectDataEvent.result = .succeeded
collectDataEvent.object = .collectedData(data)

#if SCP_RETRIEVES_COLLECTED_DATA
if let id = data.stripeId {
var retrieveCollectedDataEvent = LogEvent(method: .collectData)
self?.events.append(retrieveCollectedDataEvent)
AppDelegate.apiClient?.retrieveReaderCollectedData(id: id, completion: { json, error in
if let error = error {
retrieveCollectedDataEvent.result = .errored
retrieveCollectedDataEvent.object = .error(error as NSError)
} else if let json = json {
retrieveCollectedDataEvent.object = .json(json)
}
self?.events.append(retrieveCollectedDataEvent)
})
}
#endif
}
self?.events.append(collectDataEvent)
self?.complete()
})
}
}
2 changes: 2 additions & 0 deletions Example/Example/CustomViews.swift
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ class ReaderUpdatePicker: TextFieldView, UIPickerViewDelegate, UIPickerViewDataS
.none,
.required,
.lowBattery,
.lowBatterySucceedConnect,
.random
]

Expand All @@ -275,6 +276,7 @@ class ReaderUpdatePicker: TextFieldView, UIPickerViewDelegate, UIPickerViewDataS
.none: "No Update",
.required: "Update Required",
.lowBattery: "Update required; reader has low battery",
.lowBatterySucceedConnect: "Required update fails, reader connects",
.random: "Random"
]

Expand Down
21 changes: 21 additions & 0 deletions Example/Example/LogEventViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ struct LogEvent: CustomStringConvertible, Event {
case cancelSetupIntent = "terminal.cancelSetupIntent"
case collectInputs = "terminal.collectInputs"
case cancelCollectInputs = "terminal.cancelCollectInputs"
case collectData = "terminal.collectData"
case cancelCollectData = "terminal.cancelCollectData"
}

enum AssociatedObject {
Expand All @@ -74,6 +76,7 @@ struct LogEvent: CustomStringConvertible, Event {
case refund(Refund)
case setupIntent(SetupIntent)
case collectInputs([CollectInputsResult])
case collectedData(CollectedData)
case object(CustomStringConvertible)
}

Expand Down Expand Up @@ -251,8 +254,23 @@ struct LogEvent: CustomStringConvertible, Event {
case .errored: string = "Cancel CollectInputs Failed"
case .message(let message): string = message
}
case .collectData:
switch result {
case .started: string = "Started CollectData"
case .succeeded: string = "Completed CollectData"
case .errored: string = "CollectData Failed"
case .message(let message): string = message
}
case .cancelCollectData:
switch result {
case .started: string = "Cancel CollectData"
case .succeeded: string = "Canceled CollectData"
case .errored: string = "Cancel CollectData Failed"
case .message(let message): string = message
}
}


return string
}

Expand Down Expand Up @@ -298,6 +316,7 @@ extension LogEvent.AssociatedObject {
case .refund: return "REFUND"
case .setupIntent: return "SETUPINTENT"
case .collectInputs: return "COLLECTINPUTS"
case .collectedData: return "COLLECTDATA"
case .object: return "OBJECT"
}
}
Expand Down Expand Up @@ -354,6 +373,8 @@ extension LogEvent.AssociatedObject {
return prettyPrint(json: setupIntent.originalJSON)
case .collectInputs(let collectInputs):
return collectInputs.description
case .collectedData(let collectedData):
return collectedData.description
case .object(let object):
return object.description
}
Expand Down
5 changes: 4 additions & 1 deletion Example/Example/PaymentViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class PaymentViewController: EventDisplayingViewController {

private let paymentParams: PaymentIntentParameters
private let collectConfig: CollectConfiguration
private let confirmConfig: ConfirmConfiguration
private let declineCardBrand: CardBrand?
private let recollectAfterCardBrandDecline: Bool
private var offlineCreateConfig: CreateConfiguration?
Expand All @@ -22,6 +23,7 @@ class PaymentViewController: EventDisplayingViewController {

init(paymentParams: PaymentIntentParameters,
collectConfig: CollectConfiguration,
confirmConfig: ConfirmConfiguration,
declineCardBrand: CardBrand?,
recollectAfterCardBrandDecline: Bool,
isSposReader: Bool,
Expand All @@ -31,6 +33,7 @@ class PaymentViewController: EventDisplayingViewController {
skipCapture: Bool) {
self.paymentParams = paymentParams
self.collectConfig = collectConfig
self.confirmConfig = confirmConfig
self.declineCardBrand = declineCardBrand
self.recollectAfterCardBrandDecline = recollectAfterCardBrandDecline
self.isSposReader = isSposReader
Expand Down Expand Up @@ -207,7 +210,7 @@ class PaymentViewController: EventDisplayingViewController {
private func confirmPaymentIntent(intent: PaymentIntent) {
var processEvent = LogEvent(method: .confirmPaymentIntent)
self.events.append(processEvent)
Terminal.shared.confirmPaymentIntent(intent) { processedIntent, processError in
Terminal.shared.confirmPaymentIntent(intent, confirmConfig: self.confirmConfig) { processedIntent, processError in
if let error = processError {
processEvent.result = .errored
processEvent.object = .error(error as NSError)
Expand Down
15 changes: 13 additions & 2 deletions Example/Example/ReaderViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,11 @@ class ReaderViewController: TableViewController, CancelingViewController {
self.navigationController?.pushViewController(vc, animated: true)
}

internal func showCollectData() {
let vc = StartCollectDataViewController()
navigationController?.pushViewController(vc, animated: true)
}


internal func updateContent() {
let rdrConnectionTitle = Section.Extremity.title("Reader Connection")
Expand Down Expand Up @@ -255,17 +260,23 @@ class ReaderViewController: TableViewController, CancelingViewController {
}

if deviceType != .chipper2X && deviceType != .stripeM2 && deviceType != .appleBuiltIn {
workflowRows.append(Row(text: "In-Person Refund", detailText: "Refund a charge made by an Interac debit card.", selection: { [unowned self] in
workflowRows.append(Row(text: "In-Person refund", detailText: "Refund a charge made by an Interac debit card.", selection: { [unowned self] in
self.showStartRefund()
}, accessory: .disclosureIndicator, cellClass: SubtitleCell.self))
}

if deviceType == .wisePosE || deviceType == .wisePosEDevKit || deviceType == .stripeS700 || deviceType == .stripeS700DevKit {
workflowRows.append(Row(text: "Collect Inputs", detailText: "Collect information with forms", selection: { [unowned self] in
workflowRows.append(Row(text: "Collect inputs", detailText: "Collect information with forms", selection: { [unowned self] in
self.showCollectInputs()
}, accessory: .disclosureIndicator, cellClass: SubtitleCell.self))
}

if deviceType == .chipper2X || deviceType == .stripeM2 || deviceType == .chipper1X {
workflowRows.append(Row(text: "Collect data", detailText: "Collect non-payment data using the reader hardware.", selection: { [unowned self] in
self.showCollectData()
}, accessory: .disclosureIndicator, cellClass: SubtitleCell.self))
}

dataSource.sections = [
Section(header: "", rows: [], footer: Section.Extremity.view(headerView)),
Section(header: rdrConnectionTitle, rows: [
Expand Down
58 changes: 58 additions & 0 deletions Example/Example/StartCollectDataViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//
// StartCollectDataViewController.swift
// Example
//
// Created by Mindy Lou on 5/9/24.
// Copyright © 2024 Stripe. All rights reserved.
//

import UIKit
import Static
import StripeTerminal

class StartCollectDataViewController: TableViewController {

convenience init() {
self.init(style: .grouped)
}

override func viewDidLoad() {
super.viewDidLoad()
addKeyboardDisplayObservers()
title = "Collect Data"

updateContent()
}

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// pop if no reader is connected
guard Terminal.shared.connectedReader != nil else {
navigationController?.popViewController(animated: true)
return
}
}

internal func startCollectMagstripeData() {
do {
let collectDataConfig = try CollectDataConfigurationBuilder().setCollectDataType(.magstripe).build()
let viewController = CollectDataViewController(collectDataConfiguration: collectDataConfig)
let navigationController = LargeTitleNavigationController(rootViewController: viewController)
present(navigationController, animated: true)
} catch {
presentAlert(error: error)
}
}

private func updateContent() {
var sections = [Section]()
let dataCollectionForms = Section(rows: [
Row(text: "Collect magstripe data", selection: { [unowned self] in
startCollectMagstripeData()
}, cellClass: ButtonCell.self),
])

sections.append(dataCollectionForms)
dataSource.sections = sections
}
}
49 changes: 49 additions & 0 deletions Example/Example/StartPaymentViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,24 @@ class StartPaymentViewController: TableViewController, CancelingViewController {
return textField
}()

private lazy var surchargeNoticeTextField: TextFieldView = {
let textField = TextFieldView(placeholderText: "Surcharge Notice")
textField.textField.autocorrectionType = .no
textField.textField.autocapitalizationType = .none
textField.textField.clearButtonMode = .whileEditing
textField.textField.keyboardType = .default
return textField
}()

private lazy var amountSurchargeTextField: TextFieldView = {
let textField = TextFieldView(placeholderText: "Amount Surcharge")
textField.textField.autocorrectionType = .no
textField.textField.autocapitalizationType = .none
textField.textField.clearButtonMode = .whileEditing
textField.textField.keyboardType = .numberPad
return textField
}()

init(isSposReader: Bool) {
self.isSposReader = isSposReader
super.init(style: .grouped)
Expand Down Expand Up @@ -109,6 +127,9 @@ class StartPaymentViewController: TableViewController, CancelingViewController {
offlineTransactionLimitTextField.textField.delegate = self
offlineStoredTransactionLimitTextField.textField.delegate = self

surchargeNoticeTextField.textField.delegate = self
amountSurchargeTextField.textField.delegate = self

amountView.onAmountUpdated = { [unowned self] amountString in
self.startSection?.header = Section.Extremity.title(amountString)
self.updateContent()
Expand Down Expand Up @@ -188,11 +209,23 @@ class StartPaymentViewController: TableViewController, CancelingViewController {
Terminal.shared.simulatorConfiguration.simulatedTipAmount = NSNumber(value: simulatedTipAmountTextField.amount)

let updatePaymentIntent = self.updatePaymentIntent || (self.declineCardBrand != nil)
var surchargeNotice: String?
if let text = surchargeNoticeTextField.textField.text, !text.isEmpty {
surchargeNotice = text
}
let collectConfigBuilder = CollectConfigurationBuilder()
.setSkipTipping(self.skipTipping)
.setUpdatePaymentIntent(updatePaymentIntent)
.setEnableCustomerCancellation(self.enableCustomerCancellation)
.setRequestDynamicCurrencyConversion(self.requestDcc)
.setSurchargeNotice(surchargeNotice)
let confirmConfigBuilder = ConfirmConfigurationBuilder()

if let amountSurcharge = amountSurchargeTextField.textField.text {
if let parsedAmount = UInt(amountSurcharge, radix: 10) {
confirmConfigBuilder.setAmountSurcharge(parsedAmount)
}
}

do {
if let eligibleAmount = Int(tipEligibleAmountTextField.textField.text ?? "none") {
Expand All @@ -203,8 +236,10 @@ class StartPaymentViewController: TableViewController, CancelingViewController {
)
}
let collectConfig = try collectConfigBuilder.build()
let confirmConfig = try confirmConfigBuilder.build()
let vc = PaymentViewController(paymentParams: try paymentParamsBuilder.build(),
collectConfig: collectConfig,
confirmConfig: confirmConfig,
declineCardBrand: declineCardBrand,
recollectAfterCardBrandDecline: recollectAfterCardBrandDecline,
isSposReader: self.isSposReader,
Expand Down Expand Up @@ -461,6 +496,18 @@ class StartPaymentViewController: TableViewController, CancelingViewController {
return Section(header: "Request Dynamic Currency Conversion", rows: rows)
}

/// Makes the "Surcharge notice" section.
private func makeSurchargeNoticeSection() -> Section {
let offlineTransactionLimitSection = Section(header: .title("Surcharge Notice"), rows: [], footer: .autoLayoutView(surchargeNoticeTextField))
return offlineTransactionLimitSection
}

/// Makes the "Amount Surcharge" section.
private func makeAmountSurchargeSection() -> Section {
let offlineTransactionLimitSection = Section(header: .title("Amount Surcharge"), rows: [], footer: .autoLayoutView(amountSurchargeTextField))
return offlineTransactionLimitSection
}

private func updateContent() {

let sections: [Section?] = [
Expand All @@ -471,6 +518,8 @@ class StartPaymentViewController: TableViewController, CancelingViewController {
self.makeSimulatedTipAmountSection(),
self.makePaymentMethodSection(),
self.makeRequestDccSection(),
self.makeSurchargeNoticeSection(),
self.makeAmountSurchargeSection(),
self.makeUpdatePaymentIntentSection(),
self.makeDestinationPaymentSection(),
self.makeApplicationFeeAmountSection(),
Expand Down
Loading

0 comments on commit 1071ee6

Please sign in to comment.