Files
Kashif 55ccce6189 feat(payment_charges): add support for collecting and refunding charges on payments (#4628)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
Co-authored-by: Narayan Bhat <48803246+Narayanbhat166@users.noreply.github.com>
Co-authored-by: Shankar Singh C <83439957+ShankarSinghC@users.noreply.github.com>
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: YongJoon Kim <penubokim@gmail.com>
Co-authored-by: Sanchith Hegde <22217505+SanchithHegde@users.noreply.github.com>
Co-authored-by: chikke srujan <121822803+srujanchikke@users.noreply.github.com>
Co-authored-by: Hrithikesh <61539176+hrithikesh026@users.noreply.github.com>
Co-authored-by: Chethan Rao <70657455+Chethan-rao@users.noreply.github.com>
Co-authored-by: Sampras Lopes <Sampras.lopes@juspay.in>
2024-05-24 08:09:04 +00:00

651 lines
17 KiB
Rust

use std::str::FromStr;
pub use common_enums::*;
use utoipa::ToSchema;
#[derive(
Clone,
Copy,
Debug,
Eq,
PartialEq,
serde::Deserialize,
serde::Serialize,
strum::Display,
strum::EnumString,
)]
/// The routing algorithm to be used to process the incoming request from merchant to outgoing payment processor or payment method. The default is 'Custom'
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum RoutingAlgorithm {
RoundRobin,
MaxConversion,
MinCost,
Custom,
}
/// A connector is an integration to fulfill payments
#[derive(
Clone,
Copy,
Debug,
Eq,
PartialEq,
ToSchema,
serde::Deserialize,
serde::Serialize,
strum::VariantNames,
strum::EnumIter,
strum::Display,
strum::EnumString,
Hash,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum Connector {
#[cfg(feature = "dummy_connector")]
#[serde(rename = "phonypay")]
#[strum(serialize = "phonypay")]
DummyConnector1,
#[cfg(feature = "dummy_connector")]
#[serde(rename = "fauxpay")]
#[strum(serialize = "fauxpay")]
DummyConnector2,
#[cfg(feature = "dummy_connector")]
#[serde(rename = "pretendpay")]
#[strum(serialize = "pretendpay")]
DummyConnector3,
#[cfg(feature = "dummy_connector")]
#[serde(rename = "stripe_test")]
#[strum(serialize = "stripe_test")]
DummyConnector4,
#[cfg(feature = "dummy_connector")]
#[serde(rename = "adyen_test")]
#[strum(serialize = "adyen_test")]
DummyConnector5,
#[cfg(feature = "dummy_connector")]
#[serde(rename = "checkout_test")]
#[strum(serialize = "checkout_test")]
DummyConnector6,
#[cfg(feature = "dummy_connector")]
#[serde(rename = "paypal_test")]
#[strum(serialize = "paypal_test")]
DummyConnector7,
Aci,
Adyen,
Airwallex,
Authorizedotnet,
Bambora,
Bankofamerica,
Billwerk,
Bitpay,
Bluesnap,
Boku,
Braintree,
Cashtocode,
Checkout,
Coinbase,
Cryptopay,
Cybersource,
Dlocal,
Ebanx,
Fiserv,
Forte,
Globalpay,
Globepay,
Gocardless,
// Gpayments, Added as template code for future usage
Helcim,
Iatapay,
Klarna,
// Mifinity, Added as template code for future usage
Mollie,
Multisafepay,
Netcetera,
Nexinets,
Nmi,
Noon,
Nuvei,
// Opayo, added as template code for future usage
Opennode,
// Payeezy, As psync and rsync are not supported by this connector, it is added as template code for future usage
Payme,
// Payone, added as template code for future usage
Paypal,
Payu,
Placetopay,
Powertranz,
Prophetpay,
Rapyd,
Shift4,
Square,
Stax,
Stripe,
Threedsecureio,
Trustpay,
// Tsys,
Tsys,
Volt,
Wise,
Worldline,
Worldpay,
Signifyd,
Plaid,
Riskified,
Zen,
Zsl,
}
impl Connector {
#[cfg(feature = "payouts")]
pub fn supports_instant_payout(&self, payout_method: PayoutType) -> bool {
matches!(
(self, payout_method),
(Self::Paypal, PayoutType::Wallet) | (_, PayoutType::Card)
)
}
#[cfg(feature = "payouts")]
pub fn supports_create_recipient(&self, payout_method: PayoutType) -> bool {
matches!((self, payout_method), (_, PayoutType::Bank))
}
#[cfg(feature = "payouts")]
pub fn supports_payout_eligibility(&self, payout_method: PayoutType) -> bool {
matches!((self, payout_method), (_, PayoutType::Card))
}
#[cfg(feature = "payouts")]
pub fn supports_access_token_for_payout(&self, payout_method: PayoutType) -> bool {
matches!((self, payout_method), (Self::Paypal, _))
}
#[cfg(feature = "payouts")]
pub fn supports_vendor_disburse_account_create_for_payout(&self) -> bool {
matches!(self, Self::Stripe)
}
pub fn supports_access_token(&self, payment_method: PaymentMethod) -> bool {
matches!(
(self, payment_method),
(Self::Airwallex, _)
| (Self::Globalpay, _)
| (Self::Paypal, _)
| (Self::Payu, _)
| (Self::Trustpay, PaymentMethod::BankRedirect)
| (Self::Iatapay, _)
| (Self::Volt, _)
)
}
pub fn supports_file_storage_module(&self) -> bool {
matches!(self, Self::Stripe | Self::Checkout)
}
pub fn requires_defend_dispute(&self) -> bool {
matches!(self, Self::Checkout)
}
pub fn is_separate_authentication_supported(&self) -> bool {
match self {
#[cfg(feature = "dummy_connector")]
Self::DummyConnector1
| Self::DummyConnector2
| Self::DummyConnector3
| Self::DummyConnector4
| Self::DummyConnector5
| Self::DummyConnector6
| Self::DummyConnector7 => false,
Self::Aci
| Self::Adyen
| Self::Airwallex
| Self::Authorizedotnet
| Self::Bambora
| Self::Bankofamerica
| Self::Billwerk
| Self::Bitpay
| Self::Bluesnap
| Self::Boku
| Self::Braintree
| Self::Cashtocode
| Self::Coinbase
| Self::Cryptopay
| Self::Dlocal
| Self::Ebanx
| Self::Fiserv
| Self::Forte
| Self::Globalpay
| Self::Globepay
| Self::Gocardless
// | Self::Gpayments Added as template code for future usage
| Self::Helcim
| Self::Iatapay
| Self::Klarna
// | Self::Mifinity Added as template code for future usage
| Self::Mollie
| Self::Multisafepay
| Self::Nexinets
| Self::Nuvei
| Self::Opennode
| Self::Payme
// | Self::Payone Added as a template code for future usage
| Self::Paypal
| Self::Payu
| Self::Placetopay
| Self::Powertranz
| Self::Prophetpay
| Self::Rapyd
| Self::Shift4
| Self::Square
| Self::Stax
| Self::Trustpay
| Self::Tsys
| Self::Volt
| Self::Wise
| Self::Worldline
| Self::Worldpay
| Self::Zen
| Self::Zsl
| Self::Signifyd
| Self::Plaid
| Self::Riskified
| Self::Threedsecureio
| Self::Netcetera
| Self::Cybersource
| Self::Noon
| Self::Stripe => false,
Self::Checkout | Self::Nmi => true,
}
}
}
#[derive(
Clone,
Copy,
Debug,
Eq,
Hash,
PartialEq,
serde::Serialize,
serde::Deserialize,
strum::Display,
strum::EnumString,
ToSchema,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum AuthenticationConnectors {
Threedsecureio,
Netcetera,
Gpayments,
}
impl AuthenticationConnectors {
pub fn is_separate_version_call_required(&self) -> bool {
match self {
Self::Threedsecureio | Self::Netcetera => false,
Self::Gpayments => true,
}
}
}
#[cfg(feature = "payouts")]
#[derive(
Clone,
Copy,
Debug,
Eq,
Hash,
PartialEq,
serde::Serialize,
serde::Deserialize,
strum::Display,
strum::EnumString,
ToSchema,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum PayoutConnectors {
Adyen,
Stripe,
Wise,
Paypal,
Ebanx,
Cybersource,
}
#[cfg(feature = "payouts")]
impl From<PayoutConnectors> for RoutableConnectors {
fn from(value: PayoutConnectors) -> Self {
match value {
PayoutConnectors::Adyen => Self::Adyen,
PayoutConnectors::Stripe => Self::Stripe,
PayoutConnectors::Wise => Self::Wise,
PayoutConnectors::Paypal => Self::Paypal,
PayoutConnectors::Ebanx => Self::Ebanx,
PayoutConnectors::Cybersource => Self::Cybersource,
}
}
}
#[cfg(feature = "payouts")]
impl From<PayoutConnectors> for Connector {
fn from(value: PayoutConnectors) -> Self {
match value {
PayoutConnectors::Adyen => Self::Adyen,
PayoutConnectors::Stripe => Self::Stripe,
PayoutConnectors::Wise => Self::Wise,
PayoutConnectors::Paypal => Self::Paypal,
PayoutConnectors::Ebanx => Self::Ebanx,
PayoutConnectors::Cybersource => Self::Cybersource,
}
}
}
#[cfg(feature = "payouts")]
impl TryFrom<Connector> for PayoutConnectors {
type Error = String;
fn try_from(value: Connector) -> Result<Self, Self::Error> {
match value {
Connector::Adyen => Ok(Self::Adyen),
Connector::Stripe => Ok(Self::Stripe),
Connector::Wise => Ok(Self::Wise),
Connector::Paypal => Ok(Self::Paypal),
Connector::Ebanx => Ok(Self::Ebanx),
Connector::Cybersource => Ok(Self::Cybersource),
_ => Err(format!("Invalid payout connector {}", value)),
}
}
}
#[cfg(feature = "frm")]
#[derive(
Clone,
Copy,
Debug,
Eq,
Hash,
PartialEq,
serde::Serialize,
serde::Deserialize,
strum::Display,
strum::EnumString,
ToSchema,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum FrmConnectors {
/// Signifyd Risk Manager. Official docs: https://docs.signifyd.com/
Signifyd,
Riskified,
}
#[derive(
Clone, Debug, serde::Deserialize, serde::Serialize, strum::Display, strum::EnumString, ToSchema,
)]
#[strum(serialize_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum FrmAction {
CancelTxn,
AutoRefund,
ManualReview,
}
#[derive(
Clone, Debug, serde::Deserialize, serde::Serialize, strum::Display, strum::EnumString, ToSchema,
)]
#[strum(serialize_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum FrmPreferredFlowTypes {
Pre,
Post,
}
#[derive(Debug, Eq, PartialEq, Clone, serde::Serialize, serde::Deserialize)]
pub struct UnresolvedResponseReason {
pub code: String,
/// A message to merchant to give hint on next action he/she should do to resolve
pub message: String,
}
/// Possible field type of required fields in payment_method_data
#[derive(
Clone,
Debug,
Eq,
serde::Deserialize,
serde::Serialize,
strum::Display,
strum::EnumString,
ToSchema,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum FieldType {
UserCardNumber,
UserCardExpiryMonth,
UserCardExpiryYear,
UserCardCvc,
UserFullName,
UserEmailAddress,
UserPhoneNumber,
UserCountryCode, //phone number's country code
UserCountry { options: Vec<String> }, //for country inside payment method data ex- bank redirect
UserCurrency { options: Vec<String> },
UserBillingName,
UserAddressLine1,
UserAddressLine2,
UserAddressCity,
UserAddressPincode,
UserAddressState,
UserAddressCountry { options: Vec<String> },
UserShippingName,
UserShippingAddressLine1,
UserShippingAddressLine2,
UserShippingAddressCity,
UserShippingAddressPincode,
UserShippingAddressState,
UserShippingAddressCountry { options: Vec<String> },
UserBlikCode,
UserBank,
Text,
DropDown { options: Vec<String> },
}
impl FieldType {
pub fn get_billing_variants() -> Vec<Self> {
vec![
Self::UserBillingName,
Self::UserAddressLine1,
Self::UserAddressLine2,
Self::UserAddressCity,
Self::UserAddressPincode,
Self::UserAddressState,
Self::UserAddressCountry { options: vec![] },
]
}
pub fn get_shipping_variants() -> Vec<Self> {
vec![
Self::UserShippingName,
Self::UserShippingAddressLine1,
Self::UserShippingAddressLine2,
Self::UserShippingAddressCity,
Self::UserShippingAddressPincode,
Self::UserShippingAddressState,
Self::UserShippingAddressCountry { options: vec![] },
]
}
}
/// This implementatiobn is to ignore the inner value of UserAddressCountry enum while comparing
impl PartialEq for FieldType {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::UserCardNumber, Self::UserCardNumber) => true,
(Self::UserCardExpiryMonth, Self::UserCardExpiryMonth) => true,
(Self::UserCardExpiryYear, Self::UserCardExpiryYear) => true,
(Self::UserCardCvc, Self::UserCardCvc) => true,
(Self::UserFullName, Self::UserFullName) => true,
(Self::UserEmailAddress, Self::UserEmailAddress) => true,
(Self::UserPhoneNumber, Self::UserPhoneNumber) => true,
(Self::UserCountryCode, Self::UserCountryCode) => true,
(
Self::UserCountry {
options: options_self,
},
Self::UserCountry {
options: options_other,
},
) => options_self.eq(options_other),
(
Self::UserCurrency {
options: options_self,
},
Self::UserCurrency {
options: options_other,
},
) => options_self.eq(options_other),
(Self::UserBillingName, Self::UserBillingName) => true,
(Self::UserAddressLine1, Self::UserAddressLine1) => true,
(Self::UserAddressLine2, Self::UserAddressLine2) => true,
(Self::UserAddressCity, Self::UserAddressCity) => true,
(Self::UserAddressPincode, Self::UserAddressPincode) => true,
(Self::UserAddressState, Self::UserAddressState) => true,
(Self::UserAddressCountry { .. }, Self::UserAddressCountry { .. }) => true,
(Self::UserShippingName, Self::UserShippingName) => true,
(Self::UserShippingAddressLine1, Self::UserShippingAddressLine1) => true,
(Self::UserShippingAddressLine2, Self::UserShippingAddressLine2) => true,
(Self::UserShippingAddressCity, Self::UserShippingAddressCity) => true,
(Self::UserShippingAddressPincode, Self::UserShippingAddressPincode) => true,
(Self::UserShippingAddressState, Self::UserShippingAddressState) => true,
(Self::UserShippingAddressCountry { .. }, Self::UserShippingAddressCountry { .. }) => {
true
}
(Self::UserBlikCode, Self::UserBlikCode) => true,
(Self::UserBank, Self::UserBank) => true,
(Self::Text, Self::Text) => true,
(
Self::DropDown {
options: options_self,
},
Self::DropDown {
options: options_other,
},
) => options_self.eq(options_other),
_unused => false,
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_partialeq_for_field_type() {
let user_address_country_is_us = FieldType::UserAddressCountry {
options: vec!["US".to_string()],
};
let user_address_country_is_all = FieldType::UserAddressCountry {
options: vec!["ALL".to_string()],
};
assert!(user_address_country_is_us.eq(&user_address_country_is_all))
}
}
#[derive(
Debug,
serde::Deserialize,
serde::Serialize,
strum::Display,
strum::EnumString,
Clone,
PartialEq,
Eq,
ToSchema,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum RetryAction {
/// Payment can be retried from the client side until the payment is successful or payment expires or the attempts(configured by the merchant) for payment are exhausted
ManualRetry,
/// Denotes that the payment is requeued
Requeue,
}
#[derive(Clone, Copy)]
pub enum LockerChoice {
HyperswitchCardVault,
}
#[derive(
Clone,
Copy,
Debug,
Eq,
PartialEq,
serde::Serialize,
serde::Deserialize,
strum::Display,
strum::EnumString,
frunk::LabelledGeneric,
ToSchema,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum PmAuthConnectors {
Plaid,
}
pub fn convert_pm_auth_connector(connector_name: &str) -> Option<PmAuthConnectors> {
PmAuthConnectors::from_str(connector_name).ok()
}
pub fn convert_authentication_connector(connector_name: &str) -> Option<AuthenticationConnectors> {
AuthenticationConnectors::from_str(connector_name).ok()
}
#[derive(
Clone,
Debug,
Eq,
PartialEq,
serde::Deserialize,
serde::Serialize,
strum::Display,
strum::EnumString,
ToSchema,
Hash,
)]
pub enum PaymentChargeType {
#[serde(untagged)]
Stripe(StripeChargeType),
}
impl Default for PaymentChargeType {
fn default() -> Self {
Self::Stripe(StripeChargeType::default())
}
}
#[derive(
Clone,
Debug,
Default,
Hash,
Eq,
PartialEq,
ToSchema,
serde::Serialize,
serde::Deserialize,
strum::Display,
strum::EnumString,
)]
#[serde(rename_all = "lowercase")]
#[strum(serialize_all = "lowercase")]
pub enum StripeChargeType {
#[default]
Direct,
Destination,
}
#[cfg(feature = "frm")]
pub fn convert_frm_connector(connector_name: &str) -> Option<FrmConnectors> {
FrmConnectors::from_str(connector_name).ok()
}