Skip to content

Commit

Permalink
3.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
mindy-stripe committed Oct 10, 2023
1 parent 5fcca41 commit 6ee7b62
Show file tree
Hide file tree
Showing 591 changed files with 237,160 additions and 2,064 deletions.
7 changes: 7 additions & 0 deletions .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 16 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,23 @@ If you are using CocoaPods, update your Podfile:
```
pod 'StripeTerminal', '~> 3.0'
```
# 3.1.0 2023-10-10
* Built with Xcode 14.3, Swift version 5.8.
* New: Public beta support for offline payments.
* See [Collect payments while offline](https://stripe.com/docs/terminal/features/operate-offline/collect-payments) for details.
* Beta: Allow customer-initiated cancellation for PaymentIntent, SetupIntent, and Refund payment method collection with internet readers. See `setEnableCustomerCancellation:` on `SCPCollectConfigurationBuilder`, `SCPSetupIntentConfigurationBuilder`, and `SCPRefundConfigurationBuilder`.
* _Note: This feature requires [reader software version](https://stripe.com/docs/terminal/readers/bbpos-wisepos-e#reader-software-version) `2.17` or later to be installed on your internet reader._
* Please [contact us](mailto:stripe-terminal-betas@stripe.com) if you want to support customer-initiated cancellation.
* Update: When connecting to internet readers, the SDK no longer relies on DNS. This resolves an [error](https://support.stripe.com/questions/the-stripe-terminal-sdk-is-encountering-dns-errors-when-connecting-to-an-internet-reader) experienced by users of some DNS providers.
* Fixes an issue where tipping and offline configs may not be fetched when connecting to an mPOS reader. Tipping and offline mode users should upgrade their SDK.
* Fixes an issue where the SDK wouldn't announce an unexpected disconnect if an internet reader receives an invalid session error. This can happen after the reader reboots while the SDK is in the background.
* Fixes an issue where the SDK would error with `SCPErrorFeatureNotAvailableWithConnectedReader` instead of `SCPErrorNotConnectedToReader` when calling certain commands without being connected to a reader.
* Fixes a bug where the SDK could deadlock if attempting to connect to the same reader twice.
* Fixes a crash running `Terminal.shared.supportsReaders` on M1 Mac.
* Improved `confirmPaymentIntent` performance when location is not available.

# 3.0.0 2023-09-08
3.0.0 includes breaking changes in both symbols and behavior. See the [migration guide](https://stripe.com/docs/terminal/references/sdk-migration-guide?terminal-sdk-platform=ios) for more details.
3.0.0 includes breaking changes in both symbols and behavior. See the [migration guide](https://stripe.com/docs/terminal/references/sdk-migration-guide) for more details.

* Built with Xcode 14.3, Swift version 5.8.
* New: Private beta support for offline payments.
Expand Down
90 changes: 48 additions & 42 deletions Example/Example.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

8 changes: 3 additions & 5 deletions Example/Example/CancelableViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import UIKit
import StripeTerminal

// Used to control whether swiping to dismiss (iOS 13 only) is allowed independent of the state of the cancel button
// Used to control whether swiping to dismiss is allowed independent of the state of the cancel button
// Used in `CancelableViewController`s `setAllowedCancelMethods`.
//
// Example usage is after installing an update the Cancel button is disabled (done is enabled) but you can still swipe
Expand All @@ -24,7 +24,7 @@ struct CancelableMethods: OptionSet {
}

// Implement this on view controllers that host a cancelable. `ReaderViewController` checks for this when a
// view controller is dismissed and will call cancel. This is used as a way to handle iOS 13 swipe to dismiss.
// view controller is dismissed and will call cancel. This is used as a way to handle swipe to dismiss.
protocol CancelableViewController: UIViewController {
var cancelable: Cancelable? { get }
var cancelButton: UIBarButtonItem? { get }
Expand All @@ -49,9 +49,7 @@ extension CancelableViewController {
}

func setAllowedCancelMethods(_ allowedMethods: CancelableMethods) {
if #available(iOS 13.0, *) {
self.isModalInPresentation = !(allowedMethods.contains(.swipe))
}
self.isModalInPresentation = !(allowedMethods.contains(.swipe))
cancelButton?.isEnabled = allowedMethods.contains(.button)
}
}
Expand Down
20 changes: 20 additions & 0 deletions Example/Example/Color.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// Color.swift
// Example
//
// Created by Ben Guo on 8/3/18.
// Copyright © 2018 Stripe. All rights reserved.
//

import UIKit

extension UIColor {
static let stripeDarkGrey = UIColor(red: 32.0/255.0, green: 28.0/255.0, blue: 70.0/255.0, alpha: 255.0/255.0)
static let stripeBlue = UIColor(red: 161.0/255.0, green: 206.0/255.0, blue: 253.0/255.0, alpha: 255.0/255.0)
static let stripeOffWhite = UIColor(red: 247.0/255.0, green: 250.0/255.0, blue: 252.0/255.0, alpha: 255.0/255.0)
static let stripeLightGrey = UIColor(red: 227.0/255.0, green: 232.0/255.0, blue: 238.0/255.0, alpha: 255.0/255.0)
static let stripeGrey = UIColor(red: 193.0/255.0, green: 201.0/255.0, blue: 210.0/255.0, alpha: 255.0/255.0)
static let stripeGreen = UIColor(red: 135.0/255.0, green: 216.0/255.0, blue: 152.0/255.0, alpha: 255.0/255.0)
static let stripeRed = UIColor(red: 250.0/255.0, green: 181.0/255.0, blue: 179.0/255.0, alpha: 255.0/255.0)
static let stripeOrange = UIColor(red: 247.0/255.0, green: 185.0/255.0, blue: 137.0/255.0, alpha: 255.0/255.0)
}
22 changes: 4 additions & 18 deletions Example/Example/CustomViews.swift
Original file line number Diff line number Diff line change
Expand Up @@ -441,11 +441,7 @@ class TextFieldView: UIView {

stack.translatesAutoresizingMaskIntoConstraints = false
let insets = UIEdgeInsets.zero
if #available(iOS 11.0, *) {
stack.anchor(to: safeAreaLayoutGuide, withInsets: insets)
} else {
stack.anchorToSuperviewAnchors(withInsets: insets)
}
stack.anchor(to: safeAreaLayoutGuide, withInsets: insets)

textField.placeholder = placeholderText
}
Expand Down Expand Up @@ -566,11 +562,7 @@ class ActivityIndicatorHeaderView: UIView {

stack.translatesAutoresizingMaskIntoConstraints = false
let insets = UIEdgeInsets(top: 14, left: 16, bottom: 6, right: 16)
if #available(iOS 11.0, *) {
stack.anchor(to: safeAreaLayoutGuide, withInsets: insets)
} else {
stack.anchorToSuperviewAnchors(withInsets: insets)
}
stack.anchor(to: safeAreaLayoutGuide, withInsets: insets)

bounds.size.height = 50
}
Expand Down Expand Up @@ -638,13 +630,7 @@ open class LargeTitleNavigationController: UINavigationController {
super.viewDidLoad()

navigationBar.isTranslucent = false

if #available(iOS 11.0, *) {
navigationBar.prefersLargeTitles = true
}

if #available(iOS 13.0, *) {
navigationBar.isTranslucent = true
}
navigationBar.prefersLargeTitles = true
navigationBar.isTranslucent = true
}
}
20 changes: 3 additions & 17 deletions Example/Example/LogEventViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -257,15 +257,7 @@ struct LogEvent: CustomStringConvertible, Event {

var paymentIntentStatus: String? {
if case .paymentIntent(let intent) = object {
if intent.status == .requiresConfirmation {
return "requires_confirmation"
} else if intent.status == .requiresCapture {
return "requires_capture"
} else if let status = intent.originalJSON["status"] as? String {
return status
} else {
return "unknown"
}
return Terminal.stringFromPaymentIntentStatus(intent.status)
}
return nil
}
Expand Down Expand Up @@ -364,14 +356,8 @@ extension LogEvent.AssociatedObject {
}

do {
var data: Data
if #available(iOS 11.0, *) {
data = try JSONSerialization.data(withJSONObject: sanitizedJson,
options: [.prettyPrinted, .sortedKeys])
} else {
data = try JSONSerialization.data(withJSONObject: sanitizedJson,
options: [.prettyPrinted])
}
let data = try JSONSerialization.data(withJSONObject: sanitizedJson,
options: [.prettyPrinted, .sortedKeys])
return String(data: data, encoding: .utf8) ?? sanitizedJson.description
} catch _ {
return json.description
Expand Down
1 change: 0 additions & 1 deletion Example/Example/OfflinePaymentsLogViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import Static
import StripeTerminal

@available(iOS 11.0, *)
class OfflinePaymentsLogViewController: TableViewController {

static let directoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
Expand Down
8 changes: 4 additions & 4 deletions Example/Example/OfflineUIHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ extension OfflineUIHandler: OfflineDelegate {
case .unknown:
rightBarButtonItemView.backgroundColor = .gray
case .offline:
rightBarButtonItemView.backgroundColor = .red.withAlphaComponent(0.4)
rightBarButtonItemView.backgroundColor = .stripeOrange
case .online:
rightBarButtonItemView.backgroundColor = .green.withAlphaComponent(0.4)
rightBarButtonItemView.backgroundColor = .stripeGreen
@unknown default:
fatalError()
}
Expand All @@ -53,7 +53,7 @@ extension OfflineUIHandler: OfflineDelegate {
// Show the error right away (these may stack)
if let rootViewController = ((UIApplication.shared.delegate as? AppDelegate)?.window?.rootViewController as? RootViewController) {
let labelOverlayView = LabelOverlayView(
labelText: "⚠️ Error forwarding payment \(intent.offlineDetails()?.stripeId ?? intent.stripeId ?? "N/A")\n\(error.localizedDescription)"
labelText: "⚠️ Error forwarding payment \(intent.offlineId ?? intent.description)\n\(error.localizedDescription)"
)
rootViewController.toastView(viewToToast: labelOverlayView)
}
Expand All @@ -66,7 +66,7 @@ extension OfflineUIHandler: OfflineDelegate {
}

// If we're done, report
if Terminal.shared.offlineStatus.sdk.offlinePaymentsCount == 0 {
if Terminal.shared.offlineStatus.sdk.paymentsCount == 0 {
reportForwardCountsAndReset()
}
}
Expand Down
28 changes: 13 additions & 15 deletions Example/Example/PaymentViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,34 +17,35 @@ class PaymentViewController: EventDisplayingViewController {
private let declineCardBrand: CardBrand?
private let recollectAfterCardBrandDecline: Bool
private var offlineCreateConfig: CreateConfiguration?
private let isSposReader: Bool

init(paymentParams: PaymentIntentParameters,
collectConfig: CollectConfiguration,
declineCardBrand: CardBrand?,
recollectAfterCardBrandDecline: Bool,
isSposReader: Bool,
offlineTransactionLimit: Int,
offlineTotalTransactionLimit: Int,
forceOffline: Bool) {
offlineBehavior: OfflineBehavior) {
self.paymentParams = paymentParams
self.collectConfig = collectConfig
self.declineCardBrand = declineCardBrand
self.recollectAfterCardBrandDecline = recollectAfterCardBrandDecline
self.isSposReader = isSposReader

var isOverOfflineTransactionLimit = paymentParams.amount >= offlineTransactionLimit
if let offlinePaymentTotalByCurrency = Terminal.shared.offlineStatus.sdk.offlinePaymentAmountsByCurrency[paymentParams.currency]?.intValue {
if let offlinePaymentTotalByCurrency = Terminal.shared.offlineStatus.sdk.paymentAmountsByCurrency[paymentParams.currency]?.intValue {
isOverOfflineTransactionLimit = isOverOfflineTransactionLimit || (offlinePaymentTotalByCurrency >= offlineTotalTransactionLimit)
}
let offlineBehavior: SCPOfflineBehavior = {
if forceOffline {
return .forceOffline
} else if isOverOfflineTransactionLimit {
let offlineBehaviorFromTransactionLimit: OfflineBehavior = {
if isOverOfflineTransactionLimit {
return .requireOnline
} else {
return .preferOnline
return offlineBehavior
}
}()
do {
self.offlineCreateConfig = try CreateConfigurationBuilder().setOfflineBehavior(offlineBehavior).build()
self.offlineCreateConfig = try CreateConfigurationBuilder().setOfflineBehavior(offlineBehaviorFromTransactionLimit).build()
} catch {
fatalError("Invalid create configuration: \(error.localizedDescription)")
}
Expand Down Expand Up @@ -257,15 +258,12 @@ class PaymentViewController: EventDisplayingViewController {
}

private func writeOfflineIntentLogToDisk(_ intent: PaymentIntent) {
guard let offlineDetails = intent.offlineDetails() else { return }
let logString = "\(NSDate()) Offline payment intent \(offlineDetails.stripeId) saved to disk"
if #available(iOS 11.0, *) {
OfflinePaymentsLogViewController.writeLogToDisk(logString, details: intent)
}
let logString = "\(NSDate()) Offline payment intent \(intent.offlineId ?? intent.description) saved to disk"
OfflinePaymentsLogViewController.writeLogToDisk(logString, details: intent)
}

private func capturePaymentIntent(intent: PaymentIntent) {
if let offlineDetails = intent.offlineDetails(), offlineDetails.requiresUpload {
if let offlineDetails = intent.offlineDetails, offlineDetails.requiresUpload {
// Offline intent, not capturing.
self.writeOfflineIntentLogToDisk(intent)
self.complete()
Expand All @@ -290,7 +288,7 @@ class PaymentViewController: EventDisplayingViewController {

public func refund(chargeId: String, amount: UInt) {
self.navigationController?.pushViewController(
StartRefundViewController(chargeId: chargeId, amount: amount),
StartRefundViewController(isSposReader: self.isSposReader, chargeId: chargeId, amount: amount),
animated: true
)
}
Expand Down
6 changes: 1 addition & 5 deletions Example/Example/ReaderHeaderView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,7 @@ class ReaderHeaderView: UIView {

stack.translatesAutoresizingMaskIntoConstraints = false
let insets = UIEdgeInsets(top: 14, left: 16, bottom: 10, right: 16)
if #available(iOS 11.0, *) {
stack.anchor(to: safeAreaLayoutGuide, withInsets: insets)
} else {
stack.anchorToSuperviewAnchors(withInsets: insets)
}
stack.anchor(to: safeAreaLayoutGuide, withInsets: insets)

bounds.size.height = 110
let hardcodedImageViewHeight: CGFloat = 38
Expand Down
16 changes: 11 additions & 5 deletions Example/Example/ReaderViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -138,17 +138,23 @@ class ReaderViewController: TableViewController, CancelingViewController {
}

internal func showStartPayment() {
let vc = StartPaymentViewController()
let vc = StartPaymentViewController(isSposReader: isSposReader())
self.navigationController?.pushViewController(vc, animated: true)
}

internal func showStartRefund() {
let vc = StartRefundViewController()
let vc = StartRefundViewController(isSposReader: isSposReader())
self.navigationController?.pushViewController(vc, animated: true)
}

internal func showSetupIntent() {
self.presentModalInNavigationController(SetupIntentViewController())
let vc = StartSetupIntentViewController(isSposReader: isSposReader())
self.navigationController?.pushViewController(vc, animated: true)
}

private func isSposReader() -> Bool {
let isSposReader = [DeviceType.stripeS700, DeviceType.stripeS700DevKit, DeviceType.wisePosE, DeviceType.wisePosEDevKit, DeviceType.etna].contains(Terminal.shared.connectedReader?.deviceType)
return isSposReader
}

internal func showUpdateReader(update: ReaderSoftwareUpdate) {
Expand Down Expand Up @@ -381,10 +387,10 @@ extension ReaderViewController: OfflineDelegate {

func terminal(_ terminal: Terminal, didForwardPaymentIntent intent: PaymentIntent, error: Error?) {
if let error = error {
OfflinePaymentsLogViewController.writeLogToDisk("\(NSDate()) Error forwarding offline payment intent \(intent.offlineDetails()?.stripeId ?? intent.stripeId ?? "N/A") \(error.localizedDescription)", details: intent)
OfflinePaymentsLogViewController.writeLogToDisk("\(NSDate()) Error forwarding offline payment intent \(intent.offlineId ?? intent.description) \(error.localizedDescription)", details: intent)
return
}
OfflinePaymentsLogViewController.writeLogToDisk("\(NSDate()) Successfully forwarded offline payment intent \(intent.stripeId ?? "N/A") status: \(Terminal.stringFromPaymentIntentStatus(intent.status))", details: intent)
OfflinePaymentsLogViewController.writeLogToDisk("\(NSDate()) Successfully forwarded offline payment intent \(intent.stripeId ?? "N/A") with offline id \(intent.offlineId ?? "nil offline id") status: \(Terminal.stringFromPaymentIntentStatus(intent.status))", details: intent)
if intent.status == .requiresCapture, let id = intent.stripeId {
AppDelegate.apiClient?.capturePaymentIntent(id, completion: { error in
if let error = error {
Expand Down
6 changes: 4 additions & 2 deletions Example/Example/RefundViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ class RefundViewController: EventDisplayingViewController {
}

private let refundParameters: RefundParameters
private let refundConfig: RefundConfiguration

init(refundParams: RefundParameters) {
init(refundParams: RefundParameters, refundConfig: RefundConfiguration) {
self.refundParameters = refundParams
self.refundConfig = refundConfig
super.init()
}

Expand All @@ -33,7 +35,7 @@ class RefundViewController: EventDisplayingViewController {
// 1. collectRefundMethod
var collectEvent = LogEvent(method: .collectRefundPaymentMethod)
self.events.append(collectEvent)
self.cancelable = Terminal.shared.collectRefundPaymentMethod(self.refundParameters) { [weak self] collectError in
self.cancelable = Terminal.shared.collectRefundPaymentMethod(self.refundParameters, refundConfig: self.refundConfig) { [weak self] collectError in
guard let self = self else { return }

if let error = collectError {
Expand Down
32 changes: 19 additions & 13 deletions Example/Example/SetupIntentViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@ import Static
import StripeTerminal

class SetupIntentViewController: EventDisplayingViewController {
private let setupParams: SetupIntentParameters
private let setupConfig: SetupIntentConfiguration

init(setupParams: SetupIntentParameters, setupConfig: SetupIntentConfiguration) {
self.setupParams = setupParams
self.setupConfig = setupConfig
super.init()
}

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

override var cancelLogMethod: LogEvent.Method {
return .cancelCollectSetupIntentPaymentMethod
Expand All @@ -19,19 +31,13 @@ class SetupIntentViewController: EventDisplayingViewController {
override func viewDidLoad() {
super.viewDidLoad()

do {
let params = try SetupIntentParametersBuilder().build()

createSetupIntent(params) { intent, createError in
if createError != nil {
self.complete()
} else if let intent = intent {
// 2. collectSetupIntent
self.collectSetupIntent(intent: intent)
}
createSetupIntent(self.setupParams) { intent, createError in
if createError != nil {
self.complete()
} else if let intent = intent {
// 2. collectSetupIntent
self.collectSetupIntent(intent: intent)
}
} catch {
self.presentAlert(error: error)
}
}

Expand All @@ -55,7 +61,7 @@ class SetupIntentViewController: EventDisplayingViewController {
private func collectSetupIntent(intent: SetupIntent) {
var collectEvent = LogEvent(method: .collectSetupIntentPaymentMethod)
self.events.append(collectEvent)
self.cancelable = Terminal.shared.collectSetupIntentPaymentMethod(intent, customerConsentCollected: true) { (collectedSetupIntent, collectError) in
self.cancelable = Terminal.shared.collectSetupIntentPaymentMethod(intent, customerConsentCollected: true, setupConfig: self.setupConfig) { (collectedSetupIntent, collectError) in
if let error = collectError {
collectEvent.result = .errored
collectEvent.object = .error(error as NSError)
Expand Down
Loading

0 comments on commit 6ee7b62

Please sign in to comment.