Skip to content

Commit

Permalink
Add TransactionError event
Browse files Browse the repository at this point in the history
  • Loading branch information
mscwilson committed Jul 11, 2023
1 parent 1a42346 commit 3b745f0
Show file tree
Hide file tree
Showing 5 changed files with 358 additions and 140 deletions.
3 changes: 2 additions & 1 deletion Sources/Core/TrackerConstants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,11 @@ let kSPErrorSchema = "iglu:com.snowplowanalytics.snowplow/application_error/json
let kSPApplicationInstallSchema = "iglu:com.snowplowanalytics.mobile/application_install/jsonschema/1-0-0"
let kSPGdprContextSchema = "iglu:com.snowplowanalytics.snowplow/gdpr/jsonschema/1-0-0"
let kSPDiagnosticErrorSchema = "iglu:com.snowplowanalytics.snowplow/diagnostic_error/jsonschema/1-0-0"
let ecommerceActionSchema = "iglu:com.snowplowanalytics.snowplow.ecommerce/snowplow_ecommerce_action/jsonschema/1-0-1"
let ecommerceActionSchema = "iglu:com.snowplowanalytics.snowplow.ecommerce/snowplow_ecommerce_action/jsonschema/1-0-2"
let ecommerceProductSchema = "iglu:com.snowplowanalytics.snowplow.ecommerce/product/jsonschema/1-0-0"
let ecommerceCartSchema = "iglu:com.snowplowanalytics.snowplow.ecommerce/cart/jsonschema/1-0-0"
let ecommerceTransactionSchema = "iglu:com.snowplowanalytics.snowplow.ecommerce/transaction/jsonschema/1-0-0"
let ecommerceTransactionErrorSchema = "iglu:com.snowplowanalytics.snowplow.ecommerce/transaction_error/jsonschema/1-0-0"
let ecommerceCheckoutStepSchema = "iglu:com.snowplowanalytics.snowplow.ecommerce/checkout_step/jsonschema/1-0-0"
let ecommercePromotionSchema = "iglu:com.snowplowanalytics.snowplow.ecommerce/promotion/jsonschema/1-0-0"
let ecommerceRefundSchema = "iglu:com.snowplowanalytics.snowplow.ecommerce/refund/jsonschema/1-0-0"
Expand Down
159 changes: 159 additions & 0 deletions Sources/Snowplow/Ecommerce/Entities/TransactionEntity.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
// Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
//
// This program is licensed to you under the Apache License Version 2.0,
// and you may not use this file except in compliance with the Apache License
// Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
// http://www.apache.org/licenses/LICENSE-2.0.
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the Apache License Version 2.0 is distributed on
// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the Apache License Version 2.0 for the specific
// language governing permissions and limitations there under.

import Foundation

/**
Provided to certain Ecommerce events. The Transaction properties will be sent with the event as a Transaction entity.
Entity schema: `iglu:com.snowplowanalytics.snowplow.ecommerce/transaction/jsonschema/1-0-0`
*/
@objc(SPTransactionEntity)
public class TransactionEntity: NSObject {
/// The ID of the transaction.
@objc
public var transactionId: String

/// The total value of the transaction.
@objc
public var revenue: Decimal

/// The currency used for the transaction (ISO 4217).
@objc
public var currency: String

/// The payment method used for the transaction.
@objc
public var paymentMethod: String

/// Total quantity of items in the transaction.
@objc
public var totalQuantity: Int

/// Total amount of tax on the transaction.
public var tax: Decimal?

/// Total cost of shipping on the transaction.
public var shipping: Decimal?

/// Discount code used.
@objc
public var discountCode: String?

/// Discount amount taken off.
public var discountAmount: Decimal?

/// Whether the transaction is a credit order or not.
public var creditOrder: Bool?

internal var entity: SelfDescribingJson {
var data: [String : Any] = [
"transaction_id": transactionId,
"revenue": revenue,
"currency": currency,
"payment_method": paymentMethod,
"total_quantity": totalQuantity
]
if let tax = tax { data["tax"] = tax }
if let shipping = shipping { data["shipping"] = shipping }
if let discountCode = discountCode { data["discount_code"] = discountCode }
if let discountAmount = discountAmount { data["discount_amount"] = discountAmount }
if let creditOrder = creditOrder { data["credit_order"] = creditOrder }

return SelfDescribingJson(schema: ecommerceTransactionSchema, andData: data)
}

/// - Parameter transactionId: The ID of the transaction.
/// - Parameter revenue: The total value of the transaction.
/// - Parameter currency: The currency used (ISO 4217).
/// - Parameter paymentMethod: The payment method used.
/// - Parameter totalQuantity: Total quantity of items in the transaction.
/// - Parameter tax: Total amount of tax on the transaction.
/// - Parameter shipping: Total cost of shipping on the transaction.
/// - Parameter discountCode: Discount code used.
/// - Parameter discountAmount: Discount amount taken off.
/// - Parameter creditOrder: Whether it is a credit order or not.
public init(
transactionId: String,
revenue: Decimal,
currency: String,
paymentMethod: String,
totalQuantity: Int,
tax: Decimal? = nil,
shipping: Decimal? = nil,
discountCode: String? = nil,
discountAmount: Decimal? = nil,
creditOrder: Bool? = nil
) {
self.transactionId = transactionId
self.revenue = revenue
self.currency = currency
self.paymentMethod = paymentMethod
self.totalQuantity = totalQuantity
self.tax = tax
self.shipping = shipping
self.discountCode = discountCode
self.discountAmount = discountAmount
self.creditOrder = creditOrder
}

/// - Parameter transactionId: The ID of the transaction.
/// - Parameter revenue: The total value of the transaction.
/// - Parameter currency: The currency used (ISO 4217).
/// - Parameter paymentMethod: The payment method used.
/// - Parameter totalQuantity: Total quantity of items in the transaction.
/// - Parameter discountCode: Discount code used.
@objc
public init(
transactionId: String,
revenue: Decimal,
currency: String,
paymentMethod: String,
totalQuantity: Int,
discountCode: String? = nil
) {
self.transactionId = transactionId
self.revenue = revenue
self.currency = currency
self.paymentMethod = paymentMethod
self.totalQuantity = totalQuantity
self.discountCode = discountCode
}

/// Total amount of tax on the transaction.
@objc
public func tax(_ tax: Decimal) -> Self {
self.tax = tax
return self
}

/// Total cost of shipping on the transaction.
@objc
public func shipping(_ shipping: Decimal) -> Self {
self.shipping = shipping
return self
}

/// Discount amount taken off.
@objc
public func discountAmount(_ discountAmount: Decimal) -> Self {
self.discountAmount = discountAmount
return self
}

/// Whether the transaction is a credit order or not.
@objc
public func creditOrder(_ creditOrder: Bool) -> Self {
self.creditOrder = creditOrder
return self
}
}
133 changes: 133 additions & 0 deletions Sources/Snowplow/Ecommerce/Events/TransactionErrorEvent.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Copyright (c) 2013-2023 Snowplow Analytics Ltd. All rights reserved.
//
// This program is licensed to you under the Apache License Version 2.0,
// and you may not use this file except in compliance with the Apache License
// Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
// http://www.apache.org/licenses/LICENSE-2.0.
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the Apache License Version 2.0 is distributed on
// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the Apache License Version 2.0 for the specific
// language governing permissions and limitations there under.

import Foundation

@objc
public enum ErrorType : Int {
case hard
case soft
}

/// Track a transaction error event.
/// Entity schema: `iglu:com.snowplowanalytics.snowplow.ecommerce/transaction_error/jsonschema/1-0-0`
@objc(SPTransactionErrorEvent)
public class TransactionErrorEvent: SelfDescribingAbstract {
/// The transaction object representing the transaction that ended up in an error.
@objc
var transaction: TransactionEntity

/// Error-identifying code for the transaction issue, e.g. E522.
@objc
var errorCode: String?

/// Shortcode for the error that occurred in the transaction e.g. declined_by_stock_api, declined_by_payment_method, card_declined, pm_card_radarBlock.
@objc
var errorShortcode: String?

/// Longer description for the error that occurred in the transaction.
@objc
var errorDescription: String?

/// Type of error. Hard error types mean the customer must provide another form of payment e.g. an expired card.
/// Soft errors can be the result of temporary issues where retrying might be successful e.g. processor declined the transaction.
var errorType: ErrorType?

/// The resolution selected for the error scenario e.g. retry_allowed, user_blacklisted, block_gateway, contact_user, default.
@objc
var resolution: String?

override var schema: String {
return ecommerceActionSchema
}

override var payload: [String : Any] {
let data: [String: Any] = ["type": "trns_error"]
return data
}

override internal var entitiesForProcessing: [SelfDescribingJson]? {
get {
var entities = [SelfDescribingJson]()

var data: [String : Any] = [:]

if let errorCode = errorCode { data["error_code"] = errorCode }
if let errorShortcode = errorShortcode { data["error_shortcode"] = errorShortcode }
if let errorDescription = errorDescription { data["error_description"] = errorDescription }
if let errorType = errorType {
if (errorType == .hard) {
data["error_type"] = "hard"
} else {
data["error_type"] = "soft"
}
}
if let resolution = resolution { data["resolution"] = resolution }

entities.append(SelfDescribingJson(schema: ecommerceTransactionErrorSchema, andData: data))
entities.append(transaction.entity)

return entities
}
}

/// - Parameter transaction: The transaction object representing the transaction that ended up in an error.
/// - Parameter errorCode: Error-identifying code for the transaction issue. E.g. E522
/// - Parameter errorShortcode: Shortcode for the error that occurred in the transaction.
/// - Parameter errorDescription: Longer description for the error that occurred in the transaction.
/// - Parameter errorType: Type of error.
/// - Parameter resolution: The resolution selected for the error scenario.
public init(
transaction: TransactionEntity,
errorCode: String? = nil,
errorShortcode: String? = nil,
errorDescription: String? = nil,
errorType: ErrorType? = nil,
resolution: String? = nil
) {
self.transaction = transaction
self.errorCode = errorCode
self.errorShortcode = errorShortcode
self.errorDescription = errorDescription
self.errorType = errorType
self.resolution = resolution
}

/// - Parameter transaction: The transaction object representing the transaction that ended up in an error.
/// - Parameter errorCode: Error-identifying code for the transaction issue. E.g. E522
/// - Parameter errorShortcode: Shortcode for the error that occurred in the transaction.
/// - Parameter errorDescription: Longer description for the error that occurred in the transaction.
/// - Parameter resolution: The resolution selected for the error scenario.
@objc
public init(
transaction: TransactionEntity,
errorCode: String? = nil,
errorShortcode: String? = nil,
errorDescription: String? = nil,
resolution: String? = nil
) {
self.transaction = transaction
self.errorCode = errorCode
self.errorShortcode = errorShortcode
self.errorDescription = errorDescription
self.resolution = resolution
}

/// Type of error. Hard error types mean the customer must provide another form of payment e.g. an expired card.
/// Soft errors can be the result of temporary issues where retrying might be successful e.g. processor declined the transaction.
@objc
public func errorType(_ errorType: ErrorType) -> Self {
self.errorType = errorType
return self
}
}
Loading

0 comments on commit 3b745f0

Please sign in to comment.