Skip to content

Commit 0821d1b

Browse files
feat(core): Added Reward PaymentMethod & CurrencyAuthKey for Hyperswitch <> UCS Integration (#8767)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
1 parent 97b45f7 commit 0821d1b

File tree

8 files changed

+116
-30
lines changed

8 files changed

+116
-30
lines changed

Cargo.lock

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

crates/external_services/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ lettre = "0.11.16"
5151
once_cell = "1.21.3"
5252
serde = { version = "1.0.219", features = ["derive"] }
5353
thiserror = "1.0.69"
54+
serde_json = "1.0.140"
5455
vaultrs = { version = "0.7.4", optional = true }
5556
prost = { version = "0.13", optional = true }
5657
prost-types = { version = "0.13", optional = true }
@@ -65,11 +66,12 @@ reqwest = { version = "0.11.27", features = ["rustls-tls"] }
6566
http = "0.2.12"
6667
url = { version = "2.5.4", features = ["serde"] }
6768
quick-xml = { version = "0.31.0", features = ["serialize"] }
68-
unified-connector-service-client = { git = "https://github.com/juspay/connector-service", rev = "4387a6310dc9c2693b453b455a8032623f3d6a81", package = "rust-grpc-client" }
69+
unified-connector-service-client = { git = "https://github.com/juspay/connector-service", rev = "2263c96a70f2606475ab30e6f716b2773e1fd093", package = "rust-grpc-client" }
6970

7071

7172
# First party crates
7273
common_utils = { version = "0.1.0", path = "../common_utils" }
74+
common_enums = { version = "0.1.0", path = "../common_enums" }
7375
hyperswitch_interfaces = { version = "0.1.0", path = "../hyperswitch_interfaces", default-features = false }
7476
masking = { version = "0.1.0", path = "../masking" }
7577
router_env = { version = "0.1.0", path = "../router_env", features = [

crates/external_services/src/grpc_client/unified_connector_service.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::collections::HashMap;
2+
13
use common_utils::{consts as common_utils_consts, errors::CustomResult, types::Url};
24
use error_stack::ResultExt;
35
use masking::{PeekInterface, Secret};
@@ -144,6 +146,10 @@ pub struct ConnectorAuthMetadata {
144146
/// Optional API secret used for signature or secure authentication.
145147
pub api_secret: Option<Secret<String>>,
146148

149+
/// Optional auth_key_map used for authentication.
150+
pub auth_key_map:
151+
Option<HashMap<common_enums::enums::Currency, common_utils::pii::SecretSerdeValue>>,
152+
147153
/// Id of the merchant.
148154
pub merchant_id: Secret<String>,
149155
}
@@ -381,6 +387,16 @@ pub fn build_unified_connector_service_grpc_headers(
381387
parse("api_secret", api_secret.peek())?,
382388
);
383389
}
390+
if let Some(auth_key_map) = meta.auth_key_map {
391+
let auth_key_map_str = serde_json::to_string(&auth_key_map).map_err(|error| {
392+
logger::error!(?error);
393+
UnifiedConnectorServiceError::ParsingFailed
394+
})?;
395+
metadata.append(
396+
consts::UCS_HEADER_AUTH_KEY_MAP,
397+
parse("auth_key_map", &auth_key_map_str)?,
398+
);
399+
}
384400

385401
metadata.append(
386402
common_utils_consts::X_MERCHANT_ID,

crates/external_services/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ pub mod consts {
8585

8686
/// Header key for sending the API secret in signature-based authentication.
8787
pub(crate) const UCS_HEADER_API_SECRET: &str = "x-api-secret";
88+
89+
/// Header key for sending the AUTH KEY MAP in currency-based authentication.
90+
pub(crate) const UCS_HEADER_AUTH_KEY_MAP: &str = "x-auth-key-map";
8891
}
8992

9093
/// Metrics for interactions with external systems.

crates/router/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ reqwest = { version = "0.11.27", features = ["json", "rustls-tls", "gzip", "mult
8989
ring = "0.17.14"
9090
rust_decimal = { version = "1.37.1", features = ["serde-with-float", "serde-with-str"] }
9191
rust-i18n = { git = "https://github.com/kashif-m/rust-i18n", rev = "f2d8096aaaff7a87a847c35a5394c269f75e077a" }
92-
unified-connector-service-client = { git = "https://github.com/juspay/connector-service", rev = "4387a6310dc9c2693b453b455a8032623f3d6a81", package = "rust-grpc-client" }
92+
unified-connector-service-client = { git = "https://github.com/juspay/connector-service", rev = "2263c96a70f2606475ab30e6f716b2773e1fd093", package = "rust-grpc-client" }
9393
rustc-hash = "1.1.0"
9494
rustls = "0.22"
9595
rustls-pemfile = "2"

crates/router/src/consts.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,3 +326,6 @@ pub const UCS_AUTH_BODY_KEY: &str = "body-key";
326326

327327
/// Header value indicating that header-key-based authentication is used.
328328
pub const UCS_AUTH_HEADER_KEY: &str = "header-key";
329+
330+
/// Header value indicating that currency-auth-key-based authentication is used.
331+
pub const UCS_AUTH_CURRENCY_AUTH_KEY: &str = "currency-auth-key";

crates/router/src/core/unified_connector_service.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use masking::{ExposeInterface, PeekInterface, Secret};
1818
use router_env::logger;
1919
use unified_connector_service_client::payments::{
2020
self as payments_grpc, payment_method::PaymentMethod, CardDetails, CardPaymentMethodType,
21-
PaymentServiceAuthorizeResponse,
21+
PaymentServiceAuthorizeResponse, RewardPaymentMethodType,
2222
};
2323

2424
use crate::{
@@ -325,6 +325,24 @@ pub fn build_unified_connector_service_payment_method(
325325
payment_method: Some(upi_type),
326326
})
327327
}
328+
hyperswitch_domain_models::payment_method_data::PaymentMethodData::Reward => {
329+
match payment_method_type {
330+
PaymentMethodType::ClassicReward => Ok(payments_grpc::PaymentMethod {
331+
payment_method: Some(PaymentMethod::Reward(RewardPaymentMethodType {
332+
reward_type: 1,
333+
})),
334+
}),
335+
PaymentMethodType::Evoucher => Ok(payments_grpc::PaymentMethod {
336+
payment_method: Some(PaymentMethod::Reward(RewardPaymentMethodType {
337+
reward_type: 2,
338+
})),
339+
}),
340+
_ => Err(UnifiedConnectorServiceError::NotImplemented(format!(
341+
"Unimplemented payment method subtype: {payment_method_type:?}"
342+
))
343+
.into()),
344+
}
345+
}
328346
_ => Err(UnifiedConnectorServiceError::NotImplemented(format!(
329347
"Unimplemented payment method: {payment_method_data:?}"
330348
))
@@ -385,6 +403,7 @@ pub fn build_unified_connector_service_auth_metadata(
385403
api_key: Some(api_key.clone()),
386404
key1: Some(key1.clone()),
387405
api_secret: Some(api_secret.clone()),
406+
auth_key_map: None,
388407
merchant_id: Secret::new(merchant_id.to_string()),
389408
}),
390409
ConnectorAuthType::BodyKey { api_key, key1 } => Ok(ConnectorAuthMetadata {
@@ -393,6 +412,7 @@ pub fn build_unified_connector_service_auth_metadata(
393412
api_key: Some(api_key.clone()),
394413
key1: Some(key1.clone()),
395414
api_secret: None,
415+
auth_key_map: None,
396416
merchant_id: Secret::new(merchant_id.to_string()),
397417
}),
398418
ConnectorAuthType::HeaderKey { api_key } => Ok(ConnectorAuthMetadata {
@@ -401,6 +421,16 @@ pub fn build_unified_connector_service_auth_metadata(
401421
api_key: Some(api_key.clone()),
402422
key1: None,
403423
api_secret: None,
424+
auth_key_map: None,
425+
merchant_id: Secret::new(merchant_id.to_string()),
426+
}),
427+
ConnectorAuthType::CurrencyAuthKey { auth_key_map } => Ok(ConnectorAuthMetadata {
428+
connector_name,
429+
auth_type: consts::UCS_AUTH_CURRENCY_AUTH_KEY.to_string(),
430+
api_key: None,
431+
key1: None,
432+
api_secret: None,
433+
auth_key_map: Some(auth_key_map.clone()),
404434
merchant_id: Secret::new(merchant_id.to_string()),
405435
}),
406436
_ => Err(UnifiedConnectorServiceError::FailedToObtainAuthType)

crates/router/src/core/unified_connector_service/transformers.rs

Lines changed: 55 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,16 @@ impl ForeignTryFrom<&RouterData<PSync, PaymentsSyncData, PaymentsResponseData>>
5151
})
5252
.ok();
5353

54+
let encoded_data = router_data
55+
.request
56+
.encoded_data
57+
.as_ref()
58+
.map(|data| Identifier {
59+
id_type: Some(payments_grpc::identifier::IdType::EncodedData(
60+
data.to_string(),
61+
)),
62+
});
63+
5464
let connector_ref_id = router_data
5565
.request
5666
.connector_reference_id
@@ -60,7 +70,7 @@ impl ForeignTryFrom<&RouterData<PSync, PaymentsSyncData, PaymentsResponseData>>
6070
});
6171

6272
Ok(Self {
63-
transaction_id: connector_transaction_id,
73+
transaction_id: connector_transaction_id.or(encoded_data),
6474
request_ref_id: connector_ref_id,
6575
})
6676
}
@@ -319,6 +329,19 @@ impl ForeignTryFrom<&RouterData<Authorize, PaymentsAuthorizeData, PaymentsRespon
319329
}
320330
};
321331

332+
let capture_method = router_data
333+
.request
334+
.capture_method
335+
.map(payments_grpc::CaptureMethod::foreign_try_from)
336+
.transpose()?;
337+
338+
let browser_info = router_data
339+
.request
340+
.browser_info
341+
.clone()
342+
.map(payments_grpc::BrowserInformation::foreign_try_from)
343+
.transpose()?;
344+
322345
Ok(Self {
323346
request_ref_id: Some(Identifier {
324347
id_type: Some(payments_grpc::identifier::IdType::Id(
@@ -342,6 +365,13 @@ impl ForeignTryFrom<&RouterData<Authorize, PaymentsAuthorizeData, PaymentsRespon
342365
})
343366
.unwrap_or_default(),
344367
webhook_url: router_data.request.webhook_url.clone(),
368+
capture_method: capture_method.map(|capture_method| capture_method.into()),
369+
email: router_data
370+
.request
371+
.email
372+
.clone()
373+
.map(|e| e.expose().expose()),
374+
browser_info,
345375
})
346376
}
347377
}
@@ -370,13 +400,11 @@ impl ForeignTryFrom<payments_grpc::PaymentServiceAuthorizeResponse>
370400
})
371401
});
372402

373-
let transaction_id = response.transaction_id.as_ref().and_then(|id| {
374-
id.id_type.clone().and_then(|id_type| match id_type {
375-
payments_grpc::identifier::IdType::Id(id) => Some(id),
376-
payments_grpc::identifier::IdType::EncodedData(encoded_data) => Some(encoded_data),
377-
payments_grpc::identifier::IdType::NoResponseIdMarker(_) => None,
378-
})
379-
});
403+
let resource_id: hyperswitch_domain_models::router_request_types::ResponseId = match response.transaction_id.as_ref().and_then(|id| id.id_type.clone()) {
404+
Some(payments_grpc::identifier::IdType::Id(id)) => hyperswitch_domain_models::router_request_types::ResponseId::ConnectorTransactionId(id),
405+
Some(payments_grpc::identifier::IdType::EncodedData(encoded_data)) => hyperswitch_domain_models::router_request_types::ResponseId::EncodedData(encoded_data),
406+
Some(payments_grpc::identifier::IdType::NoResponseIdMarker(_)) | None => hyperswitch_domain_models::router_request_types::ResponseId::NoResponseId,
407+
};
380408

381409
let (connector_metadata, redirection_data) = match response.redirection_data.clone() {
382410
Some(redirection_data) => match redirection_data.form_type {
@@ -423,13 +451,8 @@ impl ForeignTryFrom<payments_grpc::PaymentServiceAuthorizeResponse>
423451
})
424452
} else {
425453
Ok(PaymentsResponseData::TransactionResponse {
426-
resource_id: match transaction_id.as_ref() {
427-
Some(transaction_id) => hyperswitch_domain_models::router_request_types::ResponseId::ConnectorTransactionId(transaction_id.clone()),
428-
None => hyperswitch_domain_models::router_request_types::ResponseId::NoResponseId,
429-
},
430-
redirection_data: Box::new(
431-
redirection_data
432-
),
454+
resource_id,
455+
redirection_data: Box::new(redirection_data),
433456
mandate_reference: Box::new(None),
434457
connector_metadata,
435458
network_txn_id: response.network_txn_id.clone(),
@@ -469,6 +492,12 @@ impl ForeignTryFrom<payments_grpc::PaymentServiceGetResponse>
469492

470493
let status_code = convert_connector_service_status_code(response.status_code)?;
471494

495+
let resource_id: hyperswitch_domain_models::router_request_types::ResponseId = match response.transaction_id.as_ref().and_then(|id| id.id_type.clone()) {
496+
Some(payments_grpc::identifier::IdType::Id(id)) => hyperswitch_domain_models::router_request_types::ResponseId::ConnectorTransactionId(id),
497+
Some(payments_grpc::identifier::IdType::EncodedData(encoded_data)) => hyperswitch_domain_models::router_request_types::ResponseId::EncodedData(encoded_data),
498+
Some(payments_grpc::identifier::IdType::NoResponseIdMarker(_)) | None => hyperswitch_domain_models::router_request_types::ResponseId::NoResponseId,
499+
};
500+
472501
let response = if response.error_code.is_some() {
473502
Err(ErrorResponse {
474503
code: response.error_code().to_owned(),
@@ -483,21 +512,22 @@ impl ForeignTryFrom<payments_grpc::PaymentServiceGetResponse>
483512
})
484513
} else {
485514
Ok(PaymentsResponseData::TransactionResponse {
486-
resource_id: match connector_response_reference_id.as_ref() {
487-
Some(connector_response_reference_id) => hyperswitch_domain_models::router_request_types::ResponseId::ConnectorTransactionId(connector_response_reference_id.clone()),
488-
None => hyperswitch_domain_models::router_request_types::ResponseId::NoResponseId,
489-
},
490-
redirection_data: Box::new(
491-
None
492-
),
493-
mandate_reference: Box::new(None),
515+
resource_id,
516+
redirection_data: Box::new(None),
517+
mandate_reference: Box::new(response.mandate_reference.map(|grpc_mandate| {
518+
hyperswitch_domain_models::router_response_types::MandateReference {
519+
connector_mandate_id: grpc_mandate.mandate_id,
520+
payment_method_id: None,
521+
mandate_metadata: None,
522+
connector_mandate_request_reference_id: None,
523+
}
524+
})),
494525
connector_metadata: None,
495526
network_txn_id: response.network_txn_id.clone(),
496527
connector_response_reference_id,
497528
incremental_authorization_allowed: None,
498529
charges: None,
499-
}
500-
)
530+
})
501531
};
502532

503533
Ok(response)

0 commit comments

Comments
 (0)