use payrix_macros::PayrixEntity;
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use super::{bool_from_int_default_false, deserialize_optional_amount, deserialize_optional_i32, deserialize_string_or_int, DateMmyy, PayrixId};
#[derive(Debug, Clone, Serialize, Deserialize, PayrixEntity)]
#[payrix(create = CreateTransaction, update = UpdateTransaction)]
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
#[serde(rename_all = "camelCase")]
pub struct Transaction {
#[payrix(readonly)]
pub id: PayrixId,
#[payrix(readonly)]
#[serde(default)]
pub created: Option<String>,
#[payrix(readonly)]
#[serde(default)]
pub modified: Option<String>,
#[payrix(readonly)]
#[serde(default)]
pub creator: Option<PayrixId>,
#[payrix(readonly)]
#[serde(default)]
pub modifier: Option<PayrixId>,
#[payrix(readonly)]
#[serde(default)]
pub ip_created: Option<String>,
#[payrix(readonly)]
#[serde(default)]
pub ip_modified: Option<String>,
#[payrix(create_only, create_required, create_type = "String")]
#[serde(default)]
pub merchant: Option<PayrixId>,
#[payrix(create_only)]
#[serde(default)]
pub token: Option<String>,
#[serde(default)]
pub payment: Option<String>,
#[payrix(create_only, create_type = "String")]
#[serde(default)]
pub fortxn: Option<PayrixId>,
#[serde(default)]
pub fromtxn: Option<PayrixId>,
#[serde(default)]
pub batch: Option<PayrixId>,
#[serde(default)]
pub subscription: Option<PayrixId>,
#[serde(default)]
pub statement: Option<PayrixId>,
#[payrix(create_only, create_required)]
#[serde(rename = "type")]
pub txn_type: TransactionType,
#[payrix(readonly)]
#[serde(default)]
pub status: Option<TransactionStatus>,
#[payrix(create_only)]
#[serde(default)]
pub origin: Option<TransactionOrigin>,
#[serde(default)]
pub platform: Option<TransactionPlatform>,
#[payrix(create_only, create_required, create_type = "i64")]
#[serde(default, deserialize_with = "deserialize_optional_amount")]
pub total: Option<i64>,
#[serde(default, deserialize_with = "deserialize_optional_amount")]
pub approved: Option<i64>,
#[serde(default, deserialize_with = "deserialize_optional_amount")]
pub original_approved: Option<i64>,
#[serde(default, deserialize_with = "deserialize_optional_i32")]
pub refunded: Option<i32>,
#[serde(default, deserialize_with = "deserialize_optional_amount")]
pub settled_total: Option<i64>,
#[serde(default, deserialize_with = "deserialize_optional_amount")]
pub tax: Option<i64>,
#[serde(default, deserialize_with = "deserialize_optional_amount")]
pub surcharge: Option<i64>,
#[serde(default, deserialize_with = "deserialize_optional_amount")]
pub shipping: Option<i64>,
#[serde(default, deserialize_with = "deserialize_optional_amount")]
pub discount: Option<i64>,
#[serde(default, deserialize_with = "deserialize_optional_amount")]
pub duty: Option<i64>,
#[serde(default, deserialize_with = "deserialize_optional_amount")]
pub cashback: Option<i64>,
#[serde(default, deserialize_with = "deserialize_optional_i32")]
pub convenience_fee: Option<i32>,
#[serde(default)]
pub fee: Option<f64>,
#[payrix(create_only)]
#[serde(rename = "fee", skip_deserializing)]
pub fee_id: Option<String>,
#[serde(default, deserialize_with = "deserialize_optional_amount")]
pub tip: Option<i64>,
#[serde(default, deserialize_with = "deserialize_optional_i32")]
pub auth_date: Option<i32>,
#[serde(default, deserialize_with = "deserialize_string_or_int")]
pub captured: Option<String>,
#[serde(default, deserialize_with = "deserialize_string_or_int")]
pub settled: Option<String>,
#[serde(default, deserialize_with = "deserialize_optional_i32")]
pub funded: Option<i32>,
#[serde(default, deserialize_with = "deserialize_string_or_int")]
pub returned: Option<String>,
#[serde(default)]
pub currency: Option<String>,
#[serde(default)]
pub funding_currency: Option<String>,
#[serde(default)]
pub settled_currency: Option<String>,
#[serde(default)]
pub currency_conversion: Option<CurrencyConversion>,
#[serde(default)]
pub authorization: Option<String>,
#[serde(default)]
pub auth_code: Option<String>,
#[serde(default)]
pub authentication: Option<String>,
#[serde(default)]
pub authentication_id: Option<String>,
#[serde(default)]
pub expiration: Option<DateMmyy>,
#[serde(default)]
pub service_code: Option<String>,
#[serde(default, deserialize_with = "deserialize_optional_i32")]
pub cvv: Option<i32>,
#[serde(default)]
pub cvv_status: Option<CvvStatus>,
#[serde(default, deserialize_with = "deserialize_optional_i32")]
pub swiped: Option<i32>,
#[serde(default, deserialize_with = "deserialize_optional_i32")]
pub emv: Option<i32>,
#[serde(default, deserialize_with = "deserialize_optional_i32")]
pub signature: Option<i32>,
#[serde(default, deserialize_with = "deserialize_optional_i32")]
pub pin: Option<i32>,
#[serde(default)]
pub terminal: Option<String>,
#[serde(default)]
pub terminal_capability: Option<TerminalCapability>,
#[serde(default)]
pub entry_mode: Option<EntryMode>,
#[serde(default, deserialize_with = "deserialize_optional_i32")]
pub mobile: Option<i32>,
#[serde(default)]
pub pin_entry_capability: Option<PinEntryCapability>,
#[serde(default, deserialize_with = "deserialize_optional_i32")]
pub unattended: Option<i32>,
#[payrix(create_only)]
#[serde(default)]
pub cof_type: Option<CardOnFileType>,
#[serde(default)]
pub order: Option<String>,
#[payrix(mutable)]
#[serde(default)]
pub description: Option<String>,
#[serde(default)]
pub descriptor: Option<String>,
#[serde(default, deserialize_with = "deserialize_optional_amount")]
pub trace_number: Option<i64>,
#[payrix(create_only)]
#[serde(default)]
pub first: Option<String>,
#[payrix(create_only)]
#[serde(default)]
pub middle: Option<String>,
#[payrix(create_only)]
#[serde(default)]
pub last: Option<String>,
#[serde(default)]
pub company: Option<String>,
#[serde(default)]
pub email: Option<String>,
#[serde(default)]
pub phone: Option<String>,
#[serde(default)]
pub address1: Option<String>,
#[serde(default)]
pub address2: Option<String>,
#[serde(default)]
pub city: Option<String>,
#[serde(default)]
pub state: Option<String>,
#[serde(default)]
pub zip: Option<String>,
#[serde(default)]
pub country: Option<String>,
#[payrix(create_only)]
#[serde(default)]
pub client_ip: Option<String>,
#[serde(default)]
pub mid: Option<String>,
#[payrix(create_only)]
#[serde(default, deserialize_with = "deserialize_optional_i32")]
pub allow_partial: Option<i32>,
#[serde(default, deserialize_with = "deserialize_optional_i32")]
pub reserved: Option<i32>,
#[serde(default, deserialize_with = "deserialize_optional_i32")]
pub misused: Option<i32>,
#[serde(default)]
pub check_stage: Option<CheckStage>,
#[serde(default)]
pub unauth_reason: Option<UnauthReason>,
#[serde(default)]
pub copy_reason: Option<CopyReason>,
#[serde(default, deserialize_with = "deserialize_optional_i32")]
pub imported: Option<i32>,
#[serde(default, deserialize_with = "deserialize_optional_i32")]
pub request_sequence: Option<i32>,
#[serde(default, deserialize_with = "deserialize_optional_i32")]
pub processed_sequence: Option<i32>,
#[serde(default, deserialize_with = "deserialize_optional_i32")]
pub debt_repayment: Option<i32>,
#[serde(default, deserialize_with = "deserialize_optional_i32")]
pub funding_enabled: Option<i32>,
#[serde(default, deserialize_with = "deserialize_optional_i32")]
pub fbo: Option<i32>,
#[serde(default)]
pub txnsession: Option<String>,
#[serde(default)]
pub auth_token_customer: Option<String>,
#[serde(default)]
pub channel: Option<String>,
#[serde(default)]
pub soft_pos_id: Option<String>,
#[serde(default)]
pub soft_pos_device_type_indicator: Option<String>,
#[serde(default, deserialize_with = "deserialize_optional_i32")]
pub network_token_indicator: Option<i32>,
#[serde(default, deserialize_with = "deserialize_optional_i32")]
pub pinless_debit_conversion: Option<i32>,
#[serde(default, deserialize_with = "deserialize_optional_i32")]
pub submitted_method: Option<i32>,
#[serde(default, deserialize_with = "deserialize_optional_i32")]
pub processed_method: Option<i32>,
#[cfg(not(feature = "sqlx"))]
#[serde(default)]
pub txn_refs: Option<Vec<serde_json::Value>>,
#[payrix(mutable)]
#[serde(default, with = "bool_from_int_default_false")]
pub inactive: bool,
#[payrix(mutable)]
#[serde(default, with = "bool_from_int_default_false")]
pub frozen: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize_repr, Deserialize_repr)]
#[repr(i32)]
pub enum TransactionType {
#[default]
CreditCardSale = 1,
CreditCardAuth = 2,
CreditCardCapture = 3,
CreditCardReverseAuth = 4,
CreditCardRefund = 5,
ECheckSale = 7,
ECheckRefund = 8,
ECheckRedeposit = 11,
ECheckAccountVerification = 12,
IncrementalAuthorization = 14,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize_repr)]
#[repr(i32)]
pub enum TransactionStatus {
#[default]
Pending = 0,
Approved = 1,
Failed = 2,
Captured = 3,
Settled = 4,
Returned = 5,
}
crate::impl_flexible_i32_enum_deserialize!(TransactionStatus, [
(0, Pending),
(1, Approved),
(2, Failed),
(3, Captured),
(4, Settled),
(5, Returned),
]);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize_repr, Deserialize_repr)]
#[repr(i32)]
pub enum TransactionOrigin {
Unknown = 0,
#[default]
Terminal = 1,
Ecommerce = 2,
MailOrTelephoneOrder = 3,
ApplePay = 4,
Success3DS = 5,
Attempted3DS = 6,
Recurring = 7,
Payframe = 8,
InWriting = 9,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum TransactionPlatform {
Apple,
Elavon,
#[serde(rename = "FIRSTDATA")]
FirstData,
Google,
#[default]
Vantiv,
Vcore,
#[serde(rename = "WELLSACH")]
WellsAch,
#[serde(rename = "WELLSFARGO")]
WellsFargo,
#[serde(rename = "WFSINGLE")]
WfSingle,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum CardOnFileType {
#[default]
Single,
Scheduled,
Unscheduled,
Installment,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize_repr, Deserialize_repr)]
#[repr(i32)]
pub enum TerminalCapability {
#[default]
KeyEntryOnly = 1,
MagneticStripe = 2,
IntegratedCircuitReader = 3,
ContactlessPayment = 4,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize_repr, Deserialize_repr)]
#[repr(i32)]
pub enum EntryMode {
#[default]
ManuallyKeyed = 1,
Track1Read = 2,
Track2Read = 3,
Track1And2Read = 4,
EmvChipRead = 5,
ContactlessRead = 6,
SwipeAfterEmvFailure = 7,
ManualAfterEmvFailure = 8,
ApplePay = 9,
GooglePay = 10,
MerchantCreated = 11,
InvoicePayment = 12,
MerchantCreatedPortal = 13,
InvoicePaymentPortal = 14,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum CvvStatus {
#[default]
NotPresent,
Illegible,
NotProvided,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum PinEntryCapability {
#[default]
Unknown,
Capable,
NotCapable,
PinPadDown,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum CurrencyConversion {
#[default]
CustomerAccepted,
CustomerRejected,
NotEligible,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum CheckStage {
#[default]
Activation,
Auth,
Postauth,
Capture,
Refund,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum UnauthReason {
#[default]
Incomplete,
Timeout,
ClerkCancelled,
CustomerCancelled,
#[serde(rename = "misdispense")]
MisDispense,
HardwareFailure,
SuspectedFraud,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum CopyReason {
#[default]
Resubmission,
Reauthorization,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize_repr, Deserialize_repr)]
#[repr(i32)]
pub enum TxnResultType {
#[default]
General = 1,
FraudPrevention = 2,
Processor = 3,
CvvMismatch = 4,
AvsCheck = 5,
AavsCheck = 6,
NetworkError = 7,
ThreeDsAlert = 8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize_repr, Deserialize_repr)]
#[repr(i32)]
pub enum TxnResultCode {
#[default]
Approved = 0,
PartiallyApproved = 1,
Declined = 2,
VerificationSuccessful = 3,
VerificationUnsuccessful = 4,
ZipCodeMismatch = 5,
AddressMismatch = 6,
NameMismatch = 7,
NameAndPhoneMismatch = 8,
NameAndEmailMismatch = 9,
PhoneMismatch = 10,
PhoneAndEmailMismatch = 11,
EmailMismatch = 12,
NameNotInTxnData = 13,
NameAndPhoneNotInTxnData = 14,
NameAndEmailNotInTxnData = 15,
PhoneNotInTxnData = 16,
PhoneAndEmailNotInTxnData = 17,
EmailNotInTxnData = 18,
CustomerNotInTxnData = 19,
NonSufficientFunds = 20,
AccountInvalid = 21,
AccountUnauthorized = 22,
GeneralError = 23,
ZipNotInTxnData = 24,
ZipAndAddressNotInTxnData = 25,
AddressNotInTxnData = 26,
NotCaptured = 27,
ThreeDsPassed = 28,
ThreeDsInvalid = 29,
ThreeDsFailed = 30,
ThreeDsNotValidated = 31,
ThreeDsAuthPassedLiabilityShifted = 32,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransactionCustom {
#[serde(default)]
pub id: Option<String>,
#[serde(default)]
pub is_trust: Option<bool>,
#[serde(default)]
pub description: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn transaction_type_serialize_all_variants() {
assert_eq!(serde_json::to_string(&TransactionType::CreditCardSale).unwrap(), "1");
assert_eq!(serde_json::to_string(&TransactionType::CreditCardAuth).unwrap(), "2");
assert_eq!(serde_json::to_string(&TransactionType::CreditCardCapture).unwrap(), "3");
assert_eq!(serde_json::to_string(&TransactionType::CreditCardReverseAuth).unwrap(), "4");
assert_eq!(serde_json::to_string(&TransactionType::CreditCardRefund).unwrap(), "5");
assert_eq!(serde_json::to_string(&TransactionType::ECheckSale).unwrap(), "7");
assert_eq!(serde_json::to_string(&TransactionType::ECheckRefund).unwrap(), "8");
assert_eq!(serde_json::to_string(&TransactionType::ECheckRedeposit).unwrap(), "11");
assert_eq!(serde_json::to_string(&TransactionType::ECheckAccountVerification).unwrap(), "12");
assert_eq!(serde_json::to_string(&TransactionType::IncrementalAuthorization).unwrap(), "14");
}
#[test]
fn transaction_type_deserialize_all_variants() {
assert_eq!(serde_json::from_str::<TransactionType>("1").unwrap(), TransactionType::CreditCardSale);
assert_eq!(serde_json::from_str::<TransactionType>("2").unwrap(), TransactionType::CreditCardAuth);
assert_eq!(serde_json::from_str::<TransactionType>("3").unwrap(), TransactionType::CreditCardCapture);
assert_eq!(serde_json::from_str::<TransactionType>("4").unwrap(), TransactionType::CreditCardReverseAuth);
assert_eq!(serde_json::from_str::<TransactionType>("5").unwrap(), TransactionType::CreditCardRefund);
assert_eq!(serde_json::from_str::<TransactionType>("7").unwrap(), TransactionType::ECheckSale);
assert_eq!(serde_json::from_str::<TransactionType>("8").unwrap(), TransactionType::ECheckRefund);
assert_eq!(serde_json::from_str::<TransactionType>("11").unwrap(), TransactionType::ECheckRedeposit);
assert_eq!(serde_json::from_str::<TransactionType>("12").unwrap(), TransactionType::ECheckAccountVerification);
assert_eq!(serde_json::from_str::<TransactionType>("14").unwrap(), TransactionType::IncrementalAuthorization);
}
#[test]
fn transaction_type_default() {
assert_eq!(TransactionType::default(), TransactionType::CreditCardSale);
}
#[test]
fn transaction_type_invalid_value() {
assert!(serde_json::from_str::<TransactionType>("99").is_err());
assert!(serde_json::from_str::<TransactionType>("6").is_err());
}
#[test]
fn transaction_status_serialize_all_variants() {
assert_eq!(serde_json::to_string(&TransactionStatus::Pending).unwrap(), "0");
assert_eq!(serde_json::to_string(&TransactionStatus::Approved).unwrap(), "1");
assert_eq!(serde_json::to_string(&TransactionStatus::Failed).unwrap(), "2");
assert_eq!(serde_json::to_string(&TransactionStatus::Captured).unwrap(), "3");
assert_eq!(serde_json::to_string(&TransactionStatus::Settled).unwrap(), "4");
assert_eq!(serde_json::to_string(&TransactionStatus::Returned).unwrap(), "5");
}
#[test]
fn transaction_status_deserialize_all_variants() {
assert_eq!(serde_json::from_str::<TransactionStatus>("0").unwrap(), TransactionStatus::Pending);
assert_eq!(serde_json::from_str::<TransactionStatus>("1").unwrap(), TransactionStatus::Approved);
assert_eq!(serde_json::from_str::<TransactionStatus>("2").unwrap(), TransactionStatus::Failed);
assert_eq!(serde_json::from_str::<TransactionStatus>("3").unwrap(), TransactionStatus::Captured);
assert_eq!(serde_json::from_str::<TransactionStatus>("4").unwrap(), TransactionStatus::Settled);
assert_eq!(serde_json::from_str::<TransactionStatus>("5").unwrap(), TransactionStatus::Returned);
}
#[test]
fn transaction_status_deserialize_from_string() {
assert_eq!(serde_json::from_str::<TransactionStatus>("\"0\"").unwrap(), TransactionStatus::Pending);
assert_eq!(serde_json::from_str::<TransactionStatus>("\"1\"").unwrap(), TransactionStatus::Approved);
}
#[test]
fn transaction_status_default() {
assert_eq!(TransactionStatus::default(), TransactionStatus::Pending);
}
#[test]
fn transaction_status_invalid_value() {
assert!(serde_json::from_str::<TransactionStatus>("99").is_err());
}
#[test]
fn transaction_origin_serialize_all_variants() {
assert_eq!(serde_json::to_string(&TransactionOrigin::Unknown).unwrap(), "0");
assert_eq!(serde_json::to_string(&TransactionOrigin::Terminal).unwrap(), "1");
assert_eq!(serde_json::to_string(&TransactionOrigin::Ecommerce).unwrap(), "2");
assert_eq!(serde_json::to_string(&TransactionOrigin::MailOrTelephoneOrder).unwrap(), "3");
assert_eq!(serde_json::to_string(&TransactionOrigin::ApplePay).unwrap(), "4");
assert_eq!(serde_json::to_string(&TransactionOrigin::Success3DS).unwrap(), "5");
assert_eq!(serde_json::to_string(&TransactionOrigin::Attempted3DS).unwrap(), "6");
assert_eq!(serde_json::to_string(&TransactionOrigin::Recurring).unwrap(), "7");
assert_eq!(serde_json::to_string(&TransactionOrigin::Payframe).unwrap(), "8");
assert_eq!(serde_json::to_string(&TransactionOrigin::InWriting).unwrap(), "9");
}
#[test]
fn transaction_origin_deserialize_all_variants() {
assert_eq!(serde_json::from_str::<TransactionOrigin>("0").unwrap(), TransactionOrigin::Unknown);
assert_eq!(serde_json::from_str::<TransactionOrigin>("1").unwrap(), TransactionOrigin::Terminal);
assert_eq!(serde_json::from_str::<TransactionOrigin>("2").unwrap(), TransactionOrigin::Ecommerce);
assert_eq!(serde_json::from_str::<TransactionOrigin>("3").unwrap(), TransactionOrigin::MailOrTelephoneOrder);
assert_eq!(serde_json::from_str::<TransactionOrigin>("4").unwrap(), TransactionOrigin::ApplePay);
assert_eq!(serde_json::from_str::<TransactionOrigin>("5").unwrap(), TransactionOrigin::Success3DS);
assert_eq!(serde_json::from_str::<TransactionOrigin>("6").unwrap(), TransactionOrigin::Attempted3DS);
assert_eq!(serde_json::from_str::<TransactionOrigin>("7").unwrap(), TransactionOrigin::Recurring);
assert_eq!(serde_json::from_str::<TransactionOrigin>("8").unwrap(), TransactionOrigin::Payframe);
assert_eq!(serde_json::from_str::<TransactionOrigin>("9").unwrap(), TransactionOrigin::InWriting);
}
#[test]
fn transaction_origin_default() {
assert_eq!(TransactionOrigin::default(), TransactionOrigin::Terminal);
}
#[test]
fn transaction_origin_invalid_value() {
assert!(serde_json::from_str::<TransactionOrigin>("99").is_err());
}
#[test]
fn transaction_platform_serialize_all_variants() {
assert_eq!(serde_json::to_string(&TransactionPlatform::Apple).unwrap(), "\"APPLE\"");
assert_eq!(serde_json::to_string(&TransactionPlatform::Elavon).unwrap(), "\"ELAVON\"");
assert_eq!(serde_json::to_string(&TransactionPlatform::FirstData).unwrap(), "\"FIRSTDATA\"");
assert_eq!(serde_json::to_string(&TransactionPlatform::Google).unwrap(), "\"GOOGLE\"");
assert_eq!(serde_json::to_string(&TransactionPlatform::Vantiv).unwrap(), "\"VANTIV\"");
assert_eq!(serde_json::to_string(&TransactionPlatform::Vcore).unwrap(), "\"VCORE\"");
assert_eq!(serde_json::to_string(&TransactionPlatform::WellsAch).unwrap(), "\"WELLSACH\"");
assert_eq!(serde_json::to_string(&TransactionPlatform::WellsFargo).unwrap(), "\"WELLSFARGO\"");
assert_eq!(serde_json::to_string(&TransactionPlatform::WfSingle).unwrap(), "\"WFSINGLE\"");
}
#[test]
fn transaction_platform_deserialize_all_variants() {
assert_eq!(serde_json::from_str::<TransactionPlatform>("\"APPLE\"").unwrap(), TransactionPlatform::Apple);
assert_eq!(serde_json::from_str::<TransactionPlatform>("\"ELAVON\"").unwrap(), TransactionPlatform::Elavon);
assert_eq!(serde_json::from_str::<TransactionPlatform>("\"FIRSTDATA\"").unwrap(), TransactionPlatform::FirstData);
assert_eq!(serde_json::from_str::<TransactionPlatform>("\"GOOGLE\"").unwrap(), TransactionPlatform::Google);
assert_eq!(serde_json::from_str::<TransactionPlatform>("\"VANTIV\"").unwrap(), TransactionPlatform::Vantiv);
assert_eq!(serde_json::from_str::<TransactionPlatform>("\"VCORE\"").unwrap(), TransactionPlatform::Vcore);
assert_eq!(serde_json::from_str::<TransactionPlatform>("\"WELLSACH\"").unwrap(), TransactionPlatform::WellsAch);
assert_eq!(serde_json::from_str::<TransactionPlatform>("\"WELLSFARGO\"").unwrap(), TransactionPlatform::WellsFargo);
assert_eq!(serde_json::from_str::<TransactionPlatform>("\"WFSINGLE\"").unwrap(), TransactionPlatform::WfSingle);
}
#[test]
fn transaction_platform_default() {
assert_eq!(TransactionPlatform::default(), TransactionPlatform::Vantiv);
}
#[test]
fn transaction_platform_invalid_value() {
assert!(serde_json::from_str::<TransactionPlatform>("\"WORLDPAY\"").is_err());
assert!(serde_json::from_str::<TransactionPlatform>("\"TDBANKCA\"").is_err());
assert!(serde_json::from_str::<TransactionPlatform>("\"INVALID\"").is_err());
}
#[test]
fn card_on_file_type_serialize_all_variants() {
assert_eq!(serde_json::to_string(&CardOnFileType::Single).unwrap(), "\"single\"");
assert_eq!(serde_json::to_string(&CardOnFileType::Scheduled).unwrap(), "\"scheduled\"");
assert_eq!(serde_json::to_string(&CardOnFileType::Unscheduled).unwrap(), "\"unscheduled\"");
assert_eq!(serde_json::to_string(&CardOnFileType::Installment).unwrap(), "\"installment\"");
}
#[test]
fn card_on_file_type_deserialize_all_variants() {
assert_eq!(serde_json::from_str::<CardOnFileType>("\"single\"").unwrap(), CardOnFileType::Single);
assert_eq!(serde_json::from_str::<CardOnFileType>("\"scheduled\"").unwrap(), CardOnFileType::Scheduled);
assert_eq!(serde_json::from_str::<CardOnFileType>("\"unscheduled\"").unwrap(), CardOnFileType::Unscheduled);
assert_eq!(serde_json::from_str::<CardOnFileType>("\"installment\"").unwrap(), CardOnFileType::Installment);
}
#[test]
fn card_on_file_type_default() {
assert_eq!(CardOnFileType::default(), CardOnFileType::Single);
}
#[test]
fn card_on_file_type_invalid_value() {
assert!(serde_json::from_str::<CardOnFileType>("\"invalid\"").is_err());
}
#[test]
fn terminal_capability_serialize_all_variants() {
assert_eq!(serde_json::to_string(&TerminalCapability::KeyEntryOnly).unwrap(), "1");
assert_eq!(serde_json::to_string(&TerminalCapability::MagneticStripe).unwrap(), "2");
assert_eq!(serde_json::to_string(&TerminalCapability::IntegratedCircuitReader).unwrap(), "3");
assert_eq!(serde_json::to_string(&TerminalCapability::ContactlessPayment).unwrap(), "4");
}
#[test]
fn terminal_capability_deserialize_all_variants() {
assert_eq!(serde_json::from_str::<TerminalCapability>("1").unwrap(), TerminalCapability::KeyEntryOnly);
assert_eq!(serde_json::from_str::<TerminalCapability>("2").unwrap(), TerminalCapability::MagneticStripe);
assert_eq!(serde_json::from_str::<TerminalCapability>("3").unwrap(), TerminalCapability::IntegratedCircuitReader);
assert_eq!(serde_json::from_str::<TerminalCapability>("4").unwrap(), TerminalCapability::ContactlessPayment);
}
#[test]
fn terminal_capability_default() {
assert_eq!(TerminalCapability::default(), TerminalCapability::KeyEntryOnly);
}
#[test]
fn terminal_capability_invalid_value() {
assert!(serde_json::from_str::<TerminalCapability>("0").is_err());
assert!(serde_json::from_str::<TerminalCapability>("5").is_err());
}
#[test]
fn entry_mode_serialize_all_variants() {
assert_eq!(serde_json::to_string(&EntryMode::ManuallyKeyed).unwrap(), "1");
assert_eq!(serde_json::to_string(&EntryMode::Track1Read).unwrap(), "2");
assert_eq!(serde_json::to_string(&EntryMode::Track2Read).unwrap(), "3");
assert_eq!(serde_json::to_string(&EntryMode::Track1And2Read).unwrap(), "4");
assert_eq!(serde_json::to_string(&EntryMode::EmvChipRead).unwrap(), "5");
assert_eq!(serde_json::to_string(&EntryMode::ContactlessRead).unwrap(), "6");
assert_eq!(serde_json::to_string(&EntryMode::SwipeAfterEmvFailure).unwrap(), "7");
assert_eq!(serde_json::to_string(&EntryMode::ManualAfterEmvFailure).unwrap(), "8");
assert_eq!(serde_json::to_string(&EntryMode::ApplePay).unwrap(), "9");
assert_eq!(serde_json::to_string(&EntryMode::GooglePay).unwrap(), "10");
assert_eq!(serde_json::to_string(&EntryMode::MerchantCreated).unwrap(), "11");
assert_eq!(serde_json::to_string(&EntryMode::InvoicePayment).unwrap(), "12");
assert_eq!(serde_json::to_string(&EntryMode::MerchantCreatedPortal).unwrap(), "13");
assert_eq!(serde_json::to_string(&EntryMode::InvoicePaymentPortal).unwrap(), "14");
}
#[test]
fn entry_mode_deserialize_all_variants() {
assert_eq!(serde_json::from_str::<EntryMode>("1").unwrap(), EntryMode::ManuallyKeyed);
assert_eq!(serde_json::from_str::<EntryMode>("2").unwrap(), EntryMode::Track1Read);
assert_eq!(serde_json::from_str::<EntryMode>("3").unwrap(), EntryMode::Track2Read);
assert_eq!(serde_json::from_str::<EntryMode>("4").unwrap(), EntryMode::Track1And2Read);
assert_eq!(serde_json::from_str::<EntryMode>("5").unwrap(), EntryMode::EmvChipRead);
assert_eq!(serde_json::from_str::<EntryMode>("6").unwrap(), EntryMode::ContactlessRead);
assert_eq!(serde_json::from_str::<EntryMode>("7").unwrap(), EntryMode::SwipeAfterEmvFailure);
assert_eq!(serde_json::from_str::<EntryMode>("8").unwrap(), EntryMode::ManualAfterEmvFailure);
assert_eq!(serde_json::from_str::<EntryMode>("9").unwrap(), EntryMode::ApplePay);
assert_eq!(serde_json::from_str::<EntryMode>("10").unwrap(), EntryMode::GooglePay);
assert_eq!(serde_json::from_str::<EntryMode>("11").unwrap(), EntryMode::MerchantCreated);
assert_eq!(serde_json::from_str::<EntryMode>("12").unwrap(), EntryMode::InvoicePayment);
assert_eq!(serde_json::from_str::<EntryMode>("13").unwrap(), EntryMode::MerchantCreatedPortal);
assert_eq!(serde_json::from_str::<EntryMode>("14").unwrap(), EntryMode::InvoicePaymentPortal);
}
#[test]
fn entry_mode_default() {
assert_eq!(EntryMode::default(), EntryMode::ManuallyKeyed);
}
#[test]
fn entry_mode_invalid_value() {
assert!(serde_json::from_str::<EntryMode>("0").is_err());
assert!(serde_json::from_str::<EntryMode>("15").is_err());
}
#[test]
fn transaction_deserialize_full() {
let json = r#"{
"id": "t1_txn_12345678901234567890123",
"merchant": "t1_mer_12345678901234567890123",
"token": "t1_tok_12345678901234567890123",
"type": 1,
"total": 10000,
"approved": 10000,
"authorization": "AUTH123",
"authCode": "123456",
"authDate": 20240101,
"status": 1,
"description": "Test transaction",
"fortxn": "t1_txn_98765432109876543210987",
"fromtxn": "t1_txn_11111111111111111111111",
"batch": "t1_bat_12345678901234567890123",
"refunded": 0,
"settled": "2024-01-02 12:00:00",
"settledTotal": 10000,
"funded": 20240103,
"returned": "20240104",
"origin": 2,
"cofType": "scheduled",
"allowPartial": 1,
"subscription": "t1_sub_12345678901234567890123",
"platform": "VANTIV",
"currency": "USD",
"clientIp": "192.168.1.1",
"expiration": "1225",
"first": "John",
"middle": "Q",
"last": "Doe",
"company": "Acme Inc",
"email": "john@example.com",
"phone": "555-1234",
"address1": "123 Main St",
"address2": "Apt 4",
"city": "New York",
"state": "NY",
"zip": "10001",
"country": "USA",
"tax": 100,
"shipping": 500,
"discount": 200,
"surcharge": 50,
"created": "2024-01-01 00:00:00.0000",
"modified": "2024-01-01 12:00:00.0000",
"inactive": 0,
"frozen": 1
}"#;
let txn: Transaction = serde_json::from_str(json).unwrap();
assert_eq!(txn.id.as_str(), "t1_txn_12345678901234567890123");
assert_eq!(txn.merchant.unwrap().as_str(), "t1_mer_12345678901234567890123");
assert_eq!(txn.token.unwrap(), "t1_tok_12345678901234567890123");
assert_eq!(txn.txn_type, TransactionType::CreditCardSale);
assert_eq!(txn.total, Some(10000));
assert_eq!(txn.status, Some(TransactionStatus::Approved));
assert_eq!(txn.origin, Some(TransactionOrigin::Ecommerce));
assert_eq!(txn.cof_type, Some(CardOnFileType::Scheduled));
assert_eq!(txn.platform, Some(TransactionPlatform::Vantiv));
assert!(!txn.inactive);
assert!(txn.frozen);
}
#[test]
fn transaction_deserialize_minimal() {
let json = r#"{
"id": "t1_txn_12345678901234567890123",
"type": 1
}"#;
let txn: Transaction = serde_json::from_str(json).unwrap();
assert_eq!(txn.id.as_str(), "t1_txn_12345678901234567890123");
assert!(txn.merchant.is_none());
assert_eq!(txn.txn_type, TransactionType::CreditCardSale);
assert!(txn.token.is_none());
assert!(txn.status.is_none());
assert!(!txn.inactive);
assert!(!txn.frozen);
}
#[test]
fn transaction_bool_from_int_zero_is_false() {
let json = r#"{"id": "t1_txn_12345678901234567890123", "type": 1, "inactive": 0, "frozen": 0}"#;
let txn: Transaction = serde_json::from_str(json).unwrap();
assert!(!txn.inactive);
assert!(!txn.frozen);
}
#[test]
fn transaction_bool_from_int_one_is_true() {
let json = r#"{"id": "t1_txn_12345678901234567890123", "type": 1, "inactive": 1, "frozen": 1}"#;
let txn: Transaction = serde_json::from_str(json).unwrap();
assert!(txn.inactive);
assert!(txn.frozen);
}
#[test]
fn transaction_bool_from_int_missing_defaults_false() {
let json = r#"{"id": "t1_txn_12345678901234567890123", "type": 1}"#;
let txn: Transaction = serde_json::from_str(json).unwrap();
assert!(!txn.inactive);
assert!(!txn.frozen);
}
#[test]
fn create_transaction_serialize_full() {
let new_txn = CreateTransaction {
merchant: "t1_mer_12345678901234567890123".to_string(),
token: Some("t1_tok_12345678901234567890123".to_string()),
txn_type: TransactionType::CreditCardSale,
origin: Some(TransactionOrigin::Ecommerce),
cof_type: Some(CardOnFileType::Scheduled),
total: 10000,
description: Some("Test transaction".to_string()),
fortxn: Some("t1_txn_98765432109876543210987".to_string()),
fee_id: Some("t1_fee_12345678901234567890123".to_string()),
allow_partial: Some(1),
client_ip: Some("192.168.1.1".to_string()),
first: Some("John".to_string()),
middle: Some("Q".to_string()),
last: Some("Doe".to_string()),
inactive: Some(false),
frozen: Some(false),
};
let json = serde_json::to_string(&new_txn).unwrap();
assert!(json.contains("\"merchant\":\"t1_mer_12345678901234567890123\""));
assert!(json.contains("\"type\":1"));
assert!(json.contains("\"total\":10000"));
assert!(json.contains("\"origin\":2"));
assert!(json.contains("\"cofType\":\"scheduled\""));
assert!(json.contains("\"fee\":\"t1_fee_12345678901234567890123\""));
}
#[test]
fn transaction_deserialize_with_pinless_debit_conversion() {
let json = r#"{
"id": "t1_txn_12345678901234567890123",
"type": 1,
"pinlessDebitConversion": 2
}"#;
let txn: Transaction = serde_json::from_str(json).unwrap();
assert_eq!(txn.pinless_debit_conversion, Some(2));
}
#[test]
fn transaction_deserialize_pinless_debit_conversion_zero() {
let json = r#"{
"id": "t1_txn_12345678901234567890123",
"type": 1,
"pinlessDebitConversion": 0
}"#;
let txn: Transaction = serde_json::from_str(json).unwrap();
assert_eq!(txn.pinless_debit_conversion, Some(0));
}
#[test]
fn transaction_deserialize_with_submitted_processed_method() {
let json = r#"{
"id": "t1_txn_12345678901234567890123",
"type": 1,
"submittedMethod": 2,
"processedMethod": 7
}"#;
let txn: Transaction = serde_json::from_str(json).unwrap();
assert_eq!(txn.submitted_method, Some(2));
assert_eq!(txn.processed_method, Some(7));
}
#[test]
fn transaction_deserialize_new_fields_absent() {
let json = r#"{
"id": "t1_txn_12345678901234567890123",
"type": 1
}"#;
let txn: Transaction = serde_json::from_str(json).unwrap();
assert!(txn.pinless_debit_conversion.is_none());
assert!(txn.submitted_method.is_none());
assert!(txn.processed_method.is_none());
}
#[cfg(not(feature = "sqlx"))]
#[test]
fn transaction_deserialize_with_txn_refs() {
let json = r#"{
"id": "t1_txn_12345678901234567890123",
"type": 1,
"txnRefs": [
{"id": "t1_trf_00000000000000000000001", "ref": "REF123", "stage": "auth"},
{"id": "t1_trf_00000000000000000000002", "ref": "REF456", "stage": "capture"}
]
}"#;
let txn: Transaction = serde_json::from_str(json).unwrap();
let refs = txn.txn_refs.unwrap();
assert_eq!(refs.len(), 2);
assert_eq!(refs[0]["stage"], "auth");
assert_eq!(refs[1]["ref"], "REF456");
}
#[cfg(not(feature = "sqlx"))]
#[test]
fn transaction_deserialize_txn_refs_absent() {
let json = r#"{"id": "t1_txn_12345678901234567890123", "type": 1}"#;
let txn: Transaction = serde_json::from_str(json).unwrap();
assert!(txn.txn_refs.is_none());
}
#[test]
fn create_transaction_serialize_minimal() {
let new_txn = CreateTransaction {
merchant: "t1_mer_12345678901234567890123".to_string(),
txn_type: TransactionType::CreditCardSale,
total: 10000,
token: None,
origin: None,
cof_type: None,
description: None,
fortxn: None,
fee_id: None,
allow_partial: None,
client_ip: None,
first: None,
middle: None,
last: None,
inactive: None,
frozen: None,
};
let json = serde_json::to_string(&new_txn).unwrap();
assert!(json.contains("\"merchant\":\"t1_mer_12345678901234567890123\""));
assert!(json.contains("\"type\":1"));
assert!(json.contains("\"total\":10000"));
assert!(!json.contains("\"token\""));
assert!(!json.contains("\"origin\""));
assert!(!json.contains("\"description\""));
}
}