refactor(connector): update add connector script with new connector features (#8213)

This commit is contained in:
sweta-sharma
2025-07-01 19:34:18 +05:30
committed by GitHub
parent a6e3d2c71e
commit 2ff93ff972
5 changed files with 209 additions and 57 deletions

View File

@ -39,11 +39,17 @@ use hyperswitch_interfaces::{
types::{self, Response},
webhooks,
};
use std::sync::LazyLock;
use common_enums::enums;
use hyperswitch_interfaces::api::ConnectorSpecifications;
use hyperswitch_domain_models::router_response_types::{ConnectorInfo, SupportedPaymentMethods};
use crate::{
constants::headers,
types::ResponseRouterData,
utils,
};
use hyperswitch_domain_models::payment_method_data::PaymentMethodData;
use transformers as {{project-name | downcase}};
@ -147,13 +153,39 @@ impl ConnectorCommon for {{project-name | downcase | pascal_case}} {
reason: response.reason,
attempt_status: None,
connector_transaction_id: None,
network_advice_code: None,
network_decline_code: None,
network_error_message: None,
})
}
}
impl ConnectorValidation for {{project-name | downcase | pascal_case}}
{
//TODO: implement functions when support enabled
fn validate_mandate_payment(
&self,
_pm_type: Option<enums::PaymentMethodType>,
pm_data: PaymentMethodData,
) -> CustomResult<(), errors::ConnectorError> {
match pm_data {
PaymentMethodData::Card(_) => Err(errors::ConnectorError::NotImplemented(
"validate_mandate_payment does not support cards".to_string(),
)
.into()),
_ => Ok(()),
}
}
fn validate_psync_reference_id(
&self,
_data: &PaymentsSyncData,
_is_three_ds: bool,
_status: enums::AttemptStatus,
_connector_meta_data: Option<common_utils::pii::SecretSerdeValue>,
) -> CustomResult<(), errors::ConnectorError> {
Ok(())
}
}
impl
@ -194,7 +226,10 @@ impl
self.common_get_content_type()
}
fn get_url(&self, _req: &PaymentsAuthorizeRouterData, _connectors: &Connectors,) -> CustomResult<String,errors::ConnectorError> {
fn get_url(
&self,
_req: &PaymentsAuthorizeRouterData,
_connectors: &Connectors,) -> CustomResult<String,errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
}
@ -425,7 +460,10 @@ impl
self.common_get_content_type()
}
fn get_url(&self, _req: &RefundsRouterData<Execute>, _connectors: &Connectors,) -> CustomResult<String,errors::ConnectorError> {
fn get_url(
&self,
_req: &RefundsRouterData<Execute>,
_connectors: &Connectors,) -> CustomResult<String,errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
}
@ -487,7 +525,9 @@ impl
self.common_get_content_type()
}
fn get_url(&self, _req: &RefundSyncRouterData,_connectors: &Connectors,) -> CustomResult<String,errors::ConnectorError> {
fn get_url(
&self,
_req: &RefundSyncRouterData,_connectors: &Connectors,) -> CustomResult<String,errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
}
@ -552,5 +592,29 @@ impl webhooks::IncomingWebhook for {{project-name | downcase | pascal_case}} {
}
}
impl ConnectorSpecifications for {{project-name | downcase | pascal_case}} {}
static {{project-name | upcase}}_SUPPORTED_PAYMENT_METHODS: LazyLock<SupportedPaymentMethods> =
LazyLock::new(|| {
SupportedPaymentMethods::new()
});
static {{project-name | upcase}}_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
display_name: "{{project-name | downcase | pascal_case}}",
description: "{{project-name | downcase | pascal_case}} connector",
connector_type: enums::PaymentConnectorCategory::PaymentGateway,
};
static {{project-name | upcase}}_SUPPORTED_WEBHOOK_FLOWS: [enums::EventClass; 0] = [];
impl ConnectorSpecifications for {{project-name | downcase | pascal_case}} {
fn get_connector_about(&self) -> Option<&'static ConnectorInfo> {
Some(&{{project-name | upcase}}_CONNECTOR_INFO)
}
fn get_supported_payment_methods(&self) -> Option<&'static SupportedPaymentMethods> {
Some(&*{{project-name | upcase}}_SUPPORTED_PAYMENT_METHODS)
}
fn get_supported_webhook_flows(&self) -> Option<&'static [enums::EventClass]> {
Some(&{{project-name | upcase}}_SUPPORTED_WEBHOOK_FLOWS)
}
}

View File

@ -1,3 +1,4 @@
use hyperswitch_domain_models::payment_method_data::{Card, PaymentMethodData};
use masking::Secret;
use router::{
types::{self, api, storage::enums,
@ -12,13 +13,13 @@ impl ConnectorActions for {{project-name | downcase | pascal_case}}Test {}
impl utils::Connector for {{project-name | downcase | pascal_case}}Test {
fn get_data(&self) -> api::ConnectorData {
use router::connector::{{project-name | downcase | pascal_case}};
api::ConnectorData {
connector: Box::new({{project-name | downcase | pascal_case}}::new()),
connector_name: types::Connector::{{project-name | downcase | pascal_case}},
get_token: types::api::GetToken::Connector,
merchant_connector_id: None,
}
}
utils::construct_connector_data_old(
Box::new({{project-name | downcase | pascal_case}}::new()),
types::Connector::Plaid,
api::GetToken::Connector,
None,
)
}
fn get_auth_token(&self) -> types::ConnectorAuthType {
utils::to_connector_auth_type(
@ -287,7 +288,7 @@ async fn should_fail_payment_for_incorrect_cvc() {
let response = CONNECTOR
.make_payment(
Some(types::PaymentsAuthorizeData {
payment_method_data: types::api::PaymentMethodData::Card(api::Card {
payment_method_data: PaymentMethodData::Card(Card {
card_cvc: Secret::new("12345".to_string()),
..utils::CCardType::default().0
}),
@ -309,7 +310,7 @@ async fn should_fail_payment_for_invalid_exp_month() {
let response = CONNECTOR
.make_payment(
Some(types::PaymentsAuthorizeData {
payment_method_data: api::PaymentMethodData::Card(api::Card {
payment_method_data: PaymentMethodData::Card(Card {
card_exp_month: Secret::new("20".to_string()),
..utils::CCardType::default().0
}),
@ -331,7 +332,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() {
let response = CONNECTOR
.make_payment(
Some(types::PaymentsAuthorizeData {
payment_method_data: api::PaymentMethodData::Card(api::Card {
payment_method_data: PaymentMethodData::Card(Card {
card_exp_year: Secret::new("2000".to_string()),
..utils::CCardType::default().0
}),

View File

@ -11,10 +11,7 @@ use hyperswitch_domain_models::{
types::{PaymentsAuthorizeRouterData, RefundsRouterData},
};
use hyperswitch_interfaces::errors;
use crate::{
types::{RefundsResponseRouterData, ResponseRouterData},
utils::PaymentsAuthorizeRequestData,
};
use crate::types::{RefundsResponseRouterData, ResponseRouterData};
//TODO: Fill the struct with respective fields
pub struct {{project-name | downcase | pascal_case}}RouterData<T> {
@ -62,21 +59,12 @@ impl TryFrom<&{{project-name | downcase | pascal_case}}RouterData<&PaymentsAutho
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(item: &{{project-name | downcase | pascal_case}}RouterData<&PaymentsAuthorizeRouterData>) -> Result<Self,Self::Error> {
match item.router_data.request.payment_method_data.clone() {
PaymentMethodData::Card(req_card) => {
let card = {{project-name | downcase | pascal_case}}Card {
number: req_card.card_number,
expiry_month: req_card.card_exp_month,
expiry_year: req_card.card_exp_year,
cvc: req_card.card_cvc,
complete: item.router_data.request.is_auto_capture()?,
};
Ok(Self {
amount: item.amount.clone(),
card,
})
}
PaymentMethodData::Card(_) => {
Err(errors::ConnectorError::NotImplemented("Card payment method not implemented".to_string()).into())
},
_ => Err(errors::ConnectorError::NotImplemented("Payment method".to_string()).into()),
}
}
}
@ -99,7 +87,7 @@ impl TryFrom<&ConnectorAuthType> for {{project-name | downcase | pascal_case}}Au
}
// PaymentsResponse
//TODO: Append the remaining status flags
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum {{project-name | downcase | pascal_case}}PaymentStatus {
Succeeded,
@ -165,7 +153,7 @@ impl<F> TryFrom<&{{project-name | downcase | pascal_case}}RouterData<&RefundsRou
// Type definition for Refund Response
#[allow(dead_code)]
#[derive(Debug, Serialize, Default, Deserialize, Clone)]
#[derive(Debug, Copy, Serialize, Default, Deserialize, Clone)]
pub enum RefundStatus {
Succeeded,
Failed,
@ -229,4 +217,7 @@ pub struct {{project-name | downcase | pascal_case}}ErrorResponse {
pub code: String,
pub message: String,
pub reason: Option<String>,
pub network_advice_code: Option<String>,
pub network_decline_code: Option<String>,
pub network_error_message: Option<String>,
}

View File

@ -5132,7 +5132,7 @@ macro_rules! default_imp_for_revenue_recovery {
};
}
default_imp_for_revenue_recovery! {
default_imp_for_revenue_recovery!(
connectors::Vgs,
connectors::Aci,
connectors::Adyen,
@ -5240,7 +5240,7 @@ default_imp_for_revenue_recovery! {
connectors::Xendit,
connectors::Zen,
connectors::Zsl
}
);
#[cfg(all(feature = "v2", feature = "revenue_recovery"))]
macro_rules! default_imp_for_billing_connector_payment_sync {

View File

@ -54,40 +54,134 @@ previous_connector_camelcase="$(tr '[:lower:]' '[:upper:]' <<< ${previous_connec
sed -i'' -e "s|pub mod $previous_connector;|pub mod $previous_connector;\npub mod ${payment_gateway};|" $conn.rs
sed -i'' -e "s/};/ ${payment_gateway}::${payment_gateway_camelcase},\n};/" $conn.rs
sed -i'' -e "/pub use hyperswitch_connectors::connectors::{/ s/{/{\n ${payment_gateway}, ${payment_gateway}::${payment_gateway_camelcase},/" $src/connector.rs
sed -i'' -e "s|$previous_connector_camelcase \(.*\)|$previous_connector_camelcase \1\n\t\t\tenums::Connector::${payment_gateway_camelcase} => Ok(ConnectorEnum::Old(\Box::new(\connector::${payment_gateway_camelcase}))),|" $src/types/api.rs
sed -i'' -e "s|$previous_connector_camelcase \(.*\)|$previous_connector_camelcase \1\n\t\t\tRoutableConnectors::${payment_gateway_camelcase} => euclid_enums::Connector::${payment_gateway_camelcase},|" crates/api_models/src/routing.rs
sed -i'' -e "s/pub $previous_connector: \(.*\)/pub $previous_connector: \1\n\tpub ${payment_gateway}: ConnectorParams,/" crates/hyperswitch_interfaces/src/configs.rs
sed -i'' -e "s|$previous_connector.base_url \(.*\)|$previous_connector.base_url \1\n${payment_gateway}.base_url = \"$base_url\"|" config/development.toml config/docker_compose.toml config/config.example.toml loadtest/config/development.toml config/deployments/integration_test.toml config/deployments/production.toml config/deployments/sandbox.toml
sed -r -i'' -e "s/\"$previous_connector\",/\"$previous_connector\",\n \"${payment_gateway}\",/" config/development.toml config/docker_compose.toml config/config.example.toml loadtest/config/development.toml
sed -i '' -e "s/\(pub enum Connector {\)/\1\n\t${payment_gateway_camelcase},/" crates/api_models/src/connector_enums.rs
sed -i '' -e "/\/\/ Add Separate authentication support for connectors/{N;s/\(.*\)\n/\1\n\t\t\t| Self::${payment_gateway_camelcase}\n/;}" crates/api_models/src/connector_enums.rs
sed -i '' -e "s/\(match connector_name {\)/\1\n\t\tapi_enums::Connector::${payment_gateway_camelcase} => {${payment_gateway}::transformers::${payment_gateway_camelcase}AuthType::try_from(val)?;Ok(())}/" $src/core/admin.rs
sed -i'' -e "s/\(pub enum RoutableConnectors {\)/\1\n\t${payment_gateway_camelcase},/" crates/common_enums/src/connector_enums.rs
sed -i '' -e "s/\(pub enum Connector {\)/\1\n\t${payment_gateway_camelcase},/" crates/euclid/src/enums.rs
sed -i'' -e "s|$previous_connector_camelcase \(.*\)|$previous_connector_camelcase \1\n\t\t\tapi_enums::Connector::${payment_gateway_camelcase} => Self::${payment_gateway_camelcase},|" $src/types/transformers.rs
sed -i'' -e "s/^default_imp_for_\(.*\)/default_imp_for_\1\n\tconnectors::${payment_gateway_camelcase},/" crates/hyperswitch_connectors/src/default_implementations.rs
sed -i'' -e "s/^default_imp_for_\(.*\)/default_imp_for_\1\n\tconnectors::${payment_gateway_camelcase},/" crates/hyperswitch_connectors/src/default_implementations_v2.rs
default_impl_files=(
"crates/hyperswitch_connectors/src/default_implementations.rs"
"crates/hyperswitch_connectors/src/default_implementations_v2.rs"
)
# Inserts the new connector into macro blocks in default_implementations files.
# - If previous_connector exists in a macro, new_connector is added after it (maintaining logical order).
# - If previous_connector is missing, new_connector is added at the top of the macro block.
# - Ensures no duplicate entries and handles all default_imp macro variants.
# Iterate through all files where default implementations are defined
for file in "${default_impl_files[@]}"; do
tmpfile="${file}.tmp"
# Use AWK to parse and update macro blocks for connector registration
awk -v prev="$previous_connector_camelcase" -v new="$payment_gateway_camelcase" '
BEGIN { in_macro = 0 }
{
if ($0 ~ /^default_imp_for_.*!\s*[\({]$/) {
in_macro = 1
inserted = 0
found_prev = 0
found_new = 0
macro_lines_count = 0
delete macro_lines
macro_header = $0
macro_open = ($0 ~ /\{$/) ? "{" : "("
macro_close = (macro_open == "{") ? "}" : ");"
next
}
if (in_macro) {
if ((macro_close == "}" && $0 ~ /^[[:space:]]*}[[:space:]]*$/) ||
(macro_close == ");" && $0 ~ /^[[:space:]]*\);[[:space:]]*$/)) {
for (i = 1; i <= macro_lines_count; i++) {
line = macro_lines[i]
clean = line
gsub(/^[ \t]+/, "", clean)
gsub(/[ \t]+$/, "", clean)
if (clean == "connectors::" prev ",") found_prev = 1
if (clean == "connectors::" new ",") found_new = 1
}
print macro_header
if (!found_prev && !found_new) {
print " connectors::" new ","
inserted = 1
}
for (i = 1; i <= macro_lines_count; i++) {
line = macro_lines[i]
clean = line
gsub(/^[ \t]+/, "", clean)
gsub(/[ \t]+$/, "", clean)
print " " clean
if (!inserted && clean == "connectors::" prev ",") {
if (!found_new) {
print " connectors::" new ","
inserted = 1
}
}
}
print $0
in_macro = 0
next
}
macro_lines[++macro_lines_count] = $0
next
}
print $0
}' "$file" > "$tmpfile" && mv "$tmpfile" "$file"
done
sed -i '' -e "\$a\\
\\
[${payment_gateway}]\\
[${payment_gateway}.connector_auth.HeaderKey]\\
api_key = \\\"API Key\\\"" crates/connector_configs/toml/sandbox.toml
sed -i '' -e "\$a\\
\\
[${payment_gateway}]\\
[${payment_gateway}.connector_auth.HeaderKey]\\
api_key = \\\"API Key\\\"" crates/connector_configs/toml/development.toml
sed -i '' -e "\$a\\
\\
[${payment_gateway}]\\
[${payment_gateway}.connector_auth.HeaderKey]\\
api_key = \\\"API Key\\\"" crates/connector_configs/toml/production.toml
sed -i'' -e "s/^default_imp_for_connector_request_id!(/default_imp_for_connector_request_id!(\n connectors::${payment_gateway_camelcase},/" $src/core/payments/flows.rs
sed -i'' -e "s/^default_imp_for_fraud_check!(/default_imp_for_fraud_check!(\n connectors::${payment_gateway_camelcase},/" $src/core/payments/flows.rs
sed -i'' -e "s/^default_imp_for_connector_authentication!(/default_imp_for_connector_authentication!(\n connectors::${payment_gateway_camelcase},/" $src/core/payments/flows.rs
sed -i'' -e "/pub struct ConnectorConfig {/ s/{/{\n pub ${payment_gateway}: Option<ConnectorTomlConfig>,/" crates/connector_configs/src/connector.rs
sed -i'' -e "/pub ${previous_connector}: Option<ConnectorTomlConfig>,/a\\
pub ${payment_gateway}: Option<ConnectorTomlConfig>,
" crates/connector_configs/src/connector.rs
sed -i'' -e "/mod utils;/ s/mod utils;/mod ${payment_gateway};\nmod utils;/" crates/router/tests/connectors/main.rs
sed -i'' -e "s/^default_imp_for_new_connector_integration_payouts!(/default_imp_for_new_connector_integration_payouts!(\n connector::${payment_gateway_camelcase},/" crates/router/src/core/payments/connector_integration_v2_impls.rs
sed -i'' -e "s/^default_imp_for_new_connector_integration_frm!(/default_imp_for_new_connector_integration_frm!(\n connector::${payment_gateway_camelcase},/" crates/router/src/core/payments/connector_integration_v2_impls.rs
sed -i'' -e "s/^default_imp_for_new_connector_integration_connector_authentication!(/default_imp_for_new_connector_integration_connector_authentication!(\n connector::${payment_gateway_camelcase},/" crates/router/src/core/payments/connector_integration_v2_impls.rs
sed -i'' -e "s/\(pub enum Connector {\)/\1\n\t${payment_gateway_camelcase},/" crates/common_enums/src/connector_enums.rs
sed -i'' -e "/match self {/ s/match self {/match self {\n | Self::${payment_gateway_camelcase}/" crates/common_enums/src/connector_enums.rs
sed -i'' -e "/match routable_connector {/ s/match routable_connector {/match routable_connector {\n RoutableConnectors::${payment_gateway_camelcase} => Self::${payment_gateway_camelcase},/" crates/common_enums/src/connector_enums.rs
sed -i'' -e "/match self.connector_name {/a\\
api_enums::Connector::${payment_gateway_camelcase} => {\\
${payment_gateway}::transformers::${payment_gateway_camelcase}AuthType::try_from(self.auth_type)?;\\
Ok(())\\
},\\
" crates/router/src/core/admin.rs
sed -i'' -e "/pub ${previous_connector}: ConnectorParams,/a\\
pub ${payment_gateway}: ConnectorParams,
" crates/hyperswitch_domain_models/src/configs.rs
# Remove temporary files created in above step
rm $conn.rs-e $src/types/api.rs-e $src/configs/settings.rs-e config/development.toml-e config/docker_compose.toml-e config/config.example.toml-e loadtest/config/development.toml-e crates/api_models/src/connector_enums.rs-e crates/euclid/src/enums.rs-e crates/api_models/src/routing.rs-e $src/core/payments/flows.rs-e crates/common_enums/src/connector_enums.rs-e $src/types/transformers.rs-e $src/core/admin.rs-e crates/hyperswitch_connectors/src/default_implementations.rs-e crates/hyperswitch_connectors/src/default_implementations_v2.rs-e crates/hyperswitch_interfaces/src/configs.rs-e $src/connector.rs-e config/deployments/integration_test.toml-e config/deployments/production.toml-e config/deployments/sandbox.toml-e temp crates/connector_configs/src/connector.rs-e crates/router/tests/connectors/main.rs-e crates/router/src/core/payments/connector_integration_v2_impls.rs-e
rm $conn.rs-e $src/types/api.rs-e $src/configs/settings.rs-e config/development.toml-e config/docker_compose.toml-e config/config.example.toml-e loadtest/config/development.toml-e crates/api_models/src/connector_enums.rs-e crates/euclid/src/enums.rs-e crates/api_models/src/routing.rs-e $src/core/payments/flows.rs-e crates/common_enums/src/connector_enums.rs-e $src/types/transformers.rs-e $src/core/admin.rs-e crates/hyperswitch_connectors/src/default_implementations.rs-e crates/hyperswitch_connectors/src/default_implementations_v2.rs-e crates/hyperswitch_interfaces/src/configs.rs-e $src/connector.rs-e config/deployments/integration_test.toml-e config/deployments/production.toml-e config/deployments/sandbox.toml-e temp crates/connector_configs/src/connector.rs-e crates/router/tests/connectors/main.rs-e crates/router/src/core/payments/connector_integration_v2_impls.rs-e crates/hyperswitch_domain_models/src/configs.rs-e
cd $conn/
# Generate template files for the connector
@ -96,7 +190,9 @@ cargo generate --path ../../../../connector-template -n $payment_gateway
# Move sub files and test files to appropriate folder
mv $payment_gateway/mod.rs $payment_gateway.rs
mv $payment_gateway/test.rs ${tests}/$payment_gateway.rs
mkdir -p ../../../router/tests/connectors
mv "$payment_gateway/test.rs" ../../../router/tests/connectors/$payment_gateway.rs
# Remove changes from tests if already done for this connector
git checkout ${tests}/main.rs ${test_utils}/connector_auth.rs ${tests}/sample_auth.toml
@ -109,9 +205,9 @@ echo "\n\n[${payment_gateway}]\napi_key=\"API Key\"" >> ${tests}/sample_auth.tom
# Remove temporary files created in above step
rm ${tests}/main.rs-e ${test_utils}/connector_auth.rs-e
cargo +nightly fmt --all
cargo check
cargo check --features v1
echo "${GREEN}Successfully created connector. Running the tests of $payment_gateway.rs"
# Runs tests for the new connector
cargo test --package router --test connectors -- $payment_gateway
echo "${ORANGE}Update your credentials for $payment_gateway connector in crates/router/tests/connectors/sample_auth.toml"
echo "${ORANGE}Update your credentials for $payment_gateway connector in crates/router/tests/connectors/sample_auth.toml"