Skip to content

Commit 322985c

Browse files
authored
feat(connector): [redsys] integrate 3ds card, refund, void, capture (#309)
1 parent 38448d9 commit 322985c

File tree

31 files changed

+3819
-9
lines changed

31 files changed

+3819
-9
lines changed

.typos.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@ extend-ignore-re = [
44
"[0-9a-f]{7,40}", # ignore git commit hashes
55
]
66

7+
[default.extend-words]
8+
ede = "ede" # Encrypt-Decrypt-Encrypt (Triple DES EDE mode)
9+
710
[default.extend-identifiers]
811
ACI = "ACI" # Name of a connector
912
BA = "BA" # Bosnia and Herzegovina country code
1013
ACTIVITE = "ACTIVITE" # French translation of activity
11-
AUTORISATION = "AUTORISATION" # French translation of authorization
14+
AUTORISATION = "AUTORISATION" # French translation of authorization
1215
CAF = "CAF" # Central African Republic (ISO 3166-1 alpha-3)
1316
FO = "FO" # Faroe Islands (the) country code
1417
JOD = "JOD" # Jordan
@@ -25,4 +28,3 @@ jus = "jus" # Juspay
2528
FPR = "FPR" # Fraud Prevention Rules
2629
Eto = "Eto" # Gigadat transaction type
2730
Cpi = "Cpi" # Gigadat transaction type
28-

Cargo.lock

Lines changed: 27 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/common_enums/src/enums.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2095,6 +2095,15 @@ pub enum MitCategory {
20952095
Resubmission,
20962096
}
20972097

2098+
/// Padding schemes used for cryptographic operations
2099+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2100+
pub enum CryptoPadding {
2101+
/// PKCS7 padding - adds bytes equal to the number of padding bytes needed
2102+
PKCS7,
2103+
/// Zero padding - pads with null bytes
2104+
ZeroPadding,
2105+
}
2106+
20982107
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, strum::Display)]
20992108
#[serde(rename_all = "snake_case")]
21002109
pub enum MandateStatus {

backend/common_utils/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ rand = "0.8.5"
4040
nanoid = "0.4.0"
4141
regex = "1.11.1"
4242
semver = { version = "1.0.26", features = ["serde"] }
43+
openssl = { version = "0.10", features = ["vendored"] }
4344

4445
# Optional features
4546
[features]

backend/common_utils/src/consts.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,24 @@ pub const X_FLOW_NAME: &str = "x-flow";
5050
/// Header key for shadow mode
5151
pub const X_SHADOW_MODE: &str = "x-shadow-mode";
5252

53+
// =============================================================================
54+
// Base64 engine
55+
// =============================================================================
56+
57+
/// General purpose base64 engine
58+
pub const BASE64_ENGINE: base64::engine::GeneralPurpose = base64::engine::general_purpose::STANDARD;
59+
/// General purpose base64 engine standard nopad
60+
pub const BASE64_ENGINE_STD_NO_PAD: base64::engine::GeneralPurpose =
61+
base64::engine::general_purpose::STANDARD_NO_PAD;
62+
63+
/// URL Safe base64 engine
64+
pub const BASE64_ENGINE_URL_SAFE: base64::engine::GeneralPurpose =
65+
base64::engine::general_purpose::URL_SAFE;
66+
67+
/// URL Safe base64 engine without padding
68+
pub const BASE64_ENGINE_URL_SAFE_NO_PAD: base64::engine::GeneralPurpose =
69+
base64::engine::general_purpose::URL_SAFE_NO_PAD;
70+
5371
// =============================================================================
5472
// Test Environment Headers
5573
// =============================================================================

backend/common_utils/src/crypto.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,64 @@ pub type EncryptableName = Encryptable<Secret<String>>;
623623
/// Type alias for `Encryptable<Secret<String>>` used for `email` field
624624
pub type EncryptableEmail = Encryptable<Secret<String, pii::EmailStrategy>>;
625625

626+
/// Triple DES (3DES) encryption using EDE3 CBC mode
627+
/// Used by connectors like Redsys for signature generation
628+
pub struct TripleDesEde3CBC {
629+
padding: common_enums::CryptoPadding,
630+
iv: Vec<u8>,
631+
}
632+
633+
impl TripleDesEde3CBC {
634+
/// Triple DES key length (24 bytes = 192 bits)
635+
pub const TRIPLE_DES_KEY_LENGTH: usize = 24;
636+
/// Triple DES IV length (8 bytes = 64 bits)
637+
pub const TRIPLE_DES_IV_LENGTH: usize = 8;
638+
639+
/// Create a new TripleDesEde3CBC instance
640+
///
641+
/// # Arguments
642+
/// * `padding` - Optional padding scheme (defaults to PKCS7)
643+
/// * `iv` - Initialization vector (must be 8 bytes)
644+
///
645+
/// # Errors
646+
/// Returns `CryptoError::InvalidIvLength` if IV is not 8 bytes
647+
pub fn new(
648+
padding: Option<common_enums::CryptoPadding>,
649+
iv: Vec<u8>,
650+
) -> CustomResult<Self, errors::CryptoError> {
651+
if iv.len() != Self::TRIPLE_DES_IV_LENGTH {
652+
Err(errors::CryptoError::InvalidIvLength)?
653+
};
654+
let padding = padding.unwrap_or(common_enums::CryptoPadding::PKCS7);
655+
Ok(Self { iv, padding })
656+
}
657+
}
658+
659+
impl EncodeMessage for TripleDesEde3CBC {
660+
fn encode_message(
661+
&self,
662+
secret: &[u8],
663+
msg: &[u8],
664+
) -> CustomResult<Vec<u8>, errors::CryptoError> {
665+
if secret.len() != Self::TRIPLE_DES_KEY_LENGTH {
666+
Err(errors::CryptoError::InvalidKeyLength)?
667+
}
668+
let mut buffer = msg.to_vec();
669+
670+
// Apply zero padding if specified
671+
if let common_enums::CryptoPadding::ZeroPadding = self.padding {
672+
let pad_len = Self::TRIPLE_DES_IV_LENGTH - (buffer.len() % Self::TRIPLE_DES_IV_LENGTH);
673+
if pad_len != Self::TRIPLE_DES_IV_LENGTH {
674+
buffer.extend(vec![0u8; pad_len]);
675+
}
676+
};
677+
678+
let cipher = openssl::symm::Cipher::des_ede3_cbc();
679+
openssl::symm::encrypt(cipher, secret, Some(&self.iv), &buffer)
680+
.change_context(errors::CryptoError::EncodingFailed)
681+
}
682+
}
683+
626684
#[cfg(test)]
627685
mod crypto_tests {
628686
#![allow(clippy::expect_used)]

backend/connector-integration/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ thiserror = "1.0.69"
4343
sha2 = "0.10"
4444
rand = "0.8"
4545
jsonwebtoken = "9.2"
46+
html-escape = "0.2"
4647

4748
[lints]
4849
workspace = true

backend/connector-integration/src/connectors.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,9 @@ pub use self::nexixpay::Nexixpay;
170170
pub mod airwallex;
171171
pub use self::airwallex::Airwallex;
172172

173+
pub mod redsys;
174+
pub use self::redsys::Redsys;
175+
173176
pub mod worldpayxml;
174177
pub use self::worldpayxml::Worldpayxml;
175178

backend/connector-integration/src/connectors/adyen/test.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,9 @@ mod tests {
173173
billing_descriptor: None,
174174
enable_partial_authorization: None,
175175
locale: None,
176+
continue_redirection_url: None,
177+
redirect_response: None,
178+
threeds_method_comp_ind: None,
176179
tokenization: None,
177180
},
178181
response: Err(ErrorResponse::default()),
@@ -319,6 +322,9 @@ mod tests {
319322
billing_descriptor: None,
320323
enable_partial_authorization: None,
321324
locale: None,
325+
continue_redirection_url: None,
326+
redirect_response: None,
327+
threeds_method_comp_ind: None,
322328
tokenization: None,
323329
},
324330
response: Err(ErrorResponse::default()),

backend/connector-integration/src/connectors/calida/test.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,9 @@ mod tests {
160160
billing_descriptor: None,
161161
enable_partial_authorization: None,
162162
locale: None,
163+
continue_redirection_url: None,
164+
redirect_response: None,
165+
threeds_method_comp_ind: None,
163166
tokenization: None,
164167
},
165168
response: Err(ErrorResponse::default()),
@@ -305,6 +308,9 @@ mod tests {
305308
billing_descriptor: None,
306309
enable_partial_authorization: None,
307310
locale: None,
311+
continue_redirection_url: None,
312+
redirect_response: None,
313+
threeds_method_comp_ind: None,
308314
tokenization: None,
309315
},
310316
response: Err(ErrorResponse::default()),

0 commit comments

Comments
 (0)