feat(connector): [billwerk] implement payment and refund flows (#4245)

Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
SamraatBansal
2024-04-01 19:33:51 +05:30
committed by GitHub
parent 740749e18a
commit aecf4aeacc
20 changed files with 784 additions and 150 deletions

View File

@ -171,6 +171,7 @@ authorizedotnet.base_url = "https://apitest.authorize.net/xml/v1/request.api"
bambora.base_url = "https://api.na.bambora.com" bambora.base_url = "https://api.na.bambora.com"
bankofamerica.base_url = "https://apitest.merchant-services.bankofamerica.com/" bankofamerica.base_url = "https://apitest.merchant-services.bankofamerica.com/"
billwerk.base_url = "https://api.reepay.com/" billwerk.base_url = "https://api.reepay.com/"
billwerk.secondary_base_url = "https://card.reepay.com/"
bitpay.base_url = "https://test.bitpay.com" bitpay.base_url = "https://test.bitpay.com"
bluesnap.base_url = "https://sandbox.bluesnap.com/" bluesnap.base_url = "https://sandbox.bluesnap.com/"
bluesnap.secondary_base_url = "https://sandpay.bluesnap.com/" bluesnap.secondary_base_url = "https://sandpay.bluesnap.com/"
@ -349,6 +350,7 @@ stax = { long_lived_token = true, payment_method = "card,bank_debit" }
square = { long_lived_token = false, payment_method = "card" } square = { long_lived_token = false, payment_method = "card" }
braintree = { long_lived_token = false, payment_method = "card" } braintree = { long_lived_token = false, payment_method = "card" }
gocardless = { long_lived_token = true, payment_method = "bank_debit" } gocardless = { long_lived_token = true, payment_method = "bank_debit" }
billwerk = {long_lived_token = false, payment_method = "card"}
[temp_locker_enable_config] [temp_locker_enable_config]
stripe = { payment_method = "bank_transfer" } stripe = { payment_method = "bank_transfer" }

View File

@ -21,6 +21,7 @@ authorizedotnet.base_url = "https://apitest.authorize.net/xml/v1/request.api"
bambora.base_url = "https://api.na.bambora.com" bambora.base_url = "https://api.na.bambora.com"
bankofamerica.base_url = "https://apitest.merchant-services.bankofamerica.com/" bankofamerica.base_url = "https://apitest.merchant-services.bankofamerica.com/"
billwerk.base_url = "https://api.reepay.com/" billwerk.base_url = "https://api.reepay.com/"
billwerk.secondary_base_url = "https://card.reepay.com/"
bitpay.base_url = "https://test.bitpay.com" bitpay.base_url = "https://test.bitpay.com"
bluesnap.base_url = "https://sandbox.bluesnap.com/" bluesnap.base_url = "https://sandbox.bluesnap.com/"
bluesnap.secondary_base_url = "https://sandpay.bluesnap.com/" bluesnap.secondary_base_url = "https://sandpay.bluesnap.com/"
@ -292,6 +293,7 @@ payme = { long_lived_token = false, payment_method = "card" }
square = { long_lived_token = false, payment_method = "card" } square = { long_lived_token = false, payment_method = "card" }
stax = { long_lived_token = true, payment_method = "card,bank_debit" } stax = { long_lived_token = true, payment_method = "card,bank_debit" }
stripe = { long_lived_token = false, payment_method = "wallet", payment_method_type = { list = "google_pay", type = "disable_only" } } stripe = { long_lived_token = false, payment_method = "wallet", payment_method_type = { list = "google_pay", type = "disable_only" } }
billwerk = {long_lived_token = false, payment_method = "card"}
[webhooks] [webhooks]
outgoing_enabled = true outgoing_enabled = true

View File

@ -25,6 +25,7 @@ authorizedotnet.base_url = "https://api.authorize.net/xml/v1/request.api"
bambora.base_url = "https://api.na.bambora.com" bambora.base_url = "https://api.na.bambora.com"
bankofamerica.base_url = "https://api.merchant-services.bankofamerica.com/" bankofamerica.base_url = "https://api.merchant-services.bankofamerica.com/"
billwerk.base_url = "https://api.reepay.com/" billwerk.base_url = "https://api.reepay.com/"
billwerk.secondary_base_url = "https://card.reepay.com/"
bitpay.base_url = "https://bitpay.com" bitpay.base_url = "https://bitpay.com"
bluesnap.base_url = "https://ws.bluesnap.com/" bluesnap.base_url = "https://ws.bluesnap.com/"
bluesnap.secondary_base_url = "https://pay.bluesnap.com/" bluesnap.secondary_base_url = "https://pay.bluesnap.com/"
@ -306,6 +307,7 @@ payme = { long_lived_token = false, payment_method = "card" }
square = { long_lived_token = false, payment_method = "card" } square = { long_lived_token = false, payment_method = "card" }
stax = { long_lived_token = true, payment_method = "card,bank_debit" } stax = { long_lived_token = true, payment_method = "card,bank_debit" }
stripe = { long_lived_token = false, payment_method = "wallet", payment_method_type = { list = "google_pay", type = "disable_only" } } stripe = { long_lived_token = false, payment_method = "wallet", payment_method_type = { list = "google_pay", type = "disable_only" } }
billwerk = {long_lived_token = false, payment_method = "card"}
[webhooks] [webhooks]
outgoing_enabled = true outgoing_enabled = true

View File

@ -25,6 +25,7 @@ authorizedotnet.base_url = "https://apitest.authorize.net/xml/v1/request.api"
bambora.base_url = "https://api.na.bambora.com" bambora.base_url = "https://api.na.bambora.com"
bankofamerica.base_url = "https://apitest.merchant-services.bankofamerica.com/" bankofamerica.base_url = "https://apitest.merchant-services.bankofamerica.com/"
billwerk.base_url = "https://api.reepay.com/" billwerk.base_url = "https://api.reepay.com/"
billwerk.secondary_base_url = "https://card.reepay.com/"
bitpay.base_url = "https://test.bitpay.com" bitpay.base_url = "https://test.bitpay.com"
bluesnap.base_url = "https://sandbox.bluesnap.com/" bluesnap.base_url = "https://sandbox.bluesnap.com/"
bluesnap.secondary_base_url = "https://sandpay.bluesnap.com/" bluesnap.secondary_base_url = "https://sandpay.bluesnap.com/"
@ -308,6 +309,7 @@ payme = { long_lived_token = false, payment_method = "card" }
square = { long_lived_token = false, payment_method = "card" } square = { long_lived_token = false, payment_method = "card" }
stax = { long_lived_token = true, payment_method = "card,bank_debit" } stax = { long_lived_token = true, payment_method = "card,bank_debit" }
stripe = { long_lived_token = false, payment_method = "wallet", payment_method_type = { list = "google_pay", type = "disable_only" } } stripe = { long_lived_token = false, payment_method = "wallet", payment_method_type = { list = "google_pay", type = "disable_only" } }
billwerk = {long_lived_token = false, payment_method = "card"}
[webhooks] [webhooks]
outgoing_enabled = true outgoing_enabled = true

View File

@ -167,6 +167,7 @@ authorizedotnet.base_url = "https://apitest.authorize.net/xml/v1/request.api"
bambora.base_url = "https://api.na.bambora.com" bambora.base_url = "https://api.na.bambora.com"
bankofamerica.base_url = "https://apitest.merchant-services.bankofamerica.com/" bankofamerica.base_url = "https://apitest.merchant-services.bankofamerica.com/"
billwerk.base_url = "https://api.reepay.com/" billwerk.base_url = "https://api.reepay.com/"
billwerk.secondary_base_url = "https://card.reepay.com/"
bitpay.base_url = "https://test.bitpay.com" bitpay.base_url = "https://test.bitpay.com"
bluesnap.base_url = "https://sandbox.bluesnap.com/" bluesnap.base_url = "https://sandbox.bluesnap.com/"
bluesnap.secondary_base_url = "https://sandpay.bluesnap.com/" bluesnap.secondary_base_url = "https://sandpay.bluesnap.com/"
@ -450,6 +451,7 @@ square = { long_lived_token = false, payment_method = "card" }
braintree = { long_lived_token = false, payment_method = "card" } braintree = { long_lived_token = false, payment_method = "card" }
payme = { long_lived_token = false, payment_method = "card" } payme = { long_lived_token = false, payment_method = "card" }
gocardless = { long_lived_token = true, payment_method = "bank_debit" } gocardless = { long_lived_token = true, payment_method = "bank_debit" }
billwerk = {long_lived_token = false, payment_method = "card"}
[temp_locker_enable_config] [temp_locker_enable_config]
stripe = { payment_method = "bank_transfer" } stripe = { payment_method = "bank_transfer" }

View File

@ -105,6 +105,7 @@ authorizedotnet.base_url = "https://apitest.authorize.net/xml/v1/request.api"
bambora.base_url = "https://api.na.bambora.com" bambora.base_url = "https://api.na.bambora.com"
bankofamerica.base_url = "https://apitest.merchant-services.bankofamerica.com/" bankofamerica.base_url = "https://apitest.merchant-services.bankofamerica.com/"
billwerk.base_url = "https://api.reepay.com/" billwerk.base_url = "https://api.reepay.com/"
billwerk.secondary_base_url = "https://card.reepay.com/"
bitpay.base_url = "https://test.bitpay.com" bitpay.base_url = "https://test.bitpay.com"
bluesnap.base_url = "https://sandbox.bluesnap.com/" bluesnap.base_url = "https://sandbox.bluesnap.com/"
bluesnap.secondary_base_url = "https://sandpay.bluesnap.com/" bluesnap.secondary_base_url = "https://sandpay.bluesnap.com/"
@ -251,6 +252,7 @@ stax = { long_lived_token = true, payment_method = "card,bank_debit" }
square = { long_lived_token = false, payment_method = "card" } square = { long_lived_token = false, payment_method = "card" }
braintree = { long_lived_token = false, payment_method = "card" } braintree = { long_lived_token = false, payment_method = "card" }
gocardless = { long_lived_token = true, payment_method = "bank_debit" } gocardless = { long_lived_token = true, payment_method = "bank_debit" }
billwerk = {long_lived_token = false, payment_method = "card"}
[temp_locker_enable_config] [temp_locker_enable_config]
stripe = { payment_method = "bank_transfer" } stripe = { payment_method = "bank_transfer" }

View File

@ -78,7 +78,7 @@ pub enum Connector {
Authorizedotnet, Authorizedotnet,
Bambora, Bambora,
Bankofamerica, Bankofamerica,
// Billwerk, Added as template code for future usage Billwerk,
Bitpay, Bitpay,
Bluesnap, Bluesnap,
Boku, Boku,
@ -166,7 +166,7 @@ impl Connector {
| Self::Authorizedotnet | Self::Authorizedotnet
| Self::Bambora | Self::Bambora
| Self::Bankofamerica | Self::Bankofamerica
// | Self::Billwerk Added as template code for future usage | Self::Billwerk
| Self::Bitpay | Self::Bitpay
| Self::Bluesnap | Self::Bluesnap
| Self::Boku | Self::Boku
@ -223,7 +223,7 @@ impl Connector {
| Self::Authorizedotnet | Self::Authorizedotnet
| Self::Bambora | Self::Bambora
| Self::Bankofamerica | Self::Bankofamerica
// | Self::Billwerk Added as template for future usage | Self::Billwerk
| Self::Bitpay | Self::Bitpay
| Self::Bluesnap | Self::Bluesnap
| Self::Boku | Self::Boku

View File

@ -115,7 +115,7 @@ pub enum RoutableConnectors {
Airwallex, Airwallex,
Authorizedotnet, Authorizedotnet,
Bankofamerica, Bankofamerica,
// Billwerk, Added as template code for future usage Billwerk,
Bitpay, Bitpay,
Bambora, Bambora,
Bluesnap, Bluesnap,

View File

@ -228,7 +228,7 @@ impl ConnectorConfig {
Connector::Airwallex => Ok(connector_data.airwallex), Connector::Airwallex => Ok(connector_data.airwallex),
Connector::Authorizedotnet => Ok(connector_data.authorizedotnet), Connector::Authorizedotnet => Ok(connector_data.authorizedotnet),
Connector::Bankofamerica => Ok(connector_data.bankofamerica), Connector::Bankofamerica => Ok(connector_data.bankofamerica),
// Connector::Billwerk => Ok(connector_data.billwerk), Added as template code for future usage Connector::Billwerk => Ok(connector_data.billwerk),
Connector::Bitpay => Ok(connector_data.bitpay), Connector::Bitpay => Ok(connector_data.bitpay),
Connector::Bluesnap => Ok(connector_data.bluesnap), Connector::Bluesnap => Ok(connector_data.bluesnap),
Connector::Boku => Ok(connector_data.boku), Connector::Boku => Ok(connector_data.boku),

View File

@ -2587,4 +2587,45 @@ api_key="Api Key"
[threedsecureio.metadata] [threedsecureio.metadata]
mcc="MCC" mcc="MCC"
merchant_country_code="3 digit numeric country code" merchant_country_code="3 digit numeric country code"
merchant_name="Name of the merchant" merchant_name="Name of the merchant"
[billwerk]
[[billwerk.credit]]
payment_method_type = "Mastercard"
[[billwerk.credit]]
payment_method_type = "Visa"
[[billwerk.credit]]
payment_method_type = "Interac"
[[billwerk.credit]]
payment_method_type = "AmericanExpress"
[[billwerk.credit]]
payment_method_type = "JCB"
[[billwerk.credit]]
payment_method_type = "DinersClub"
[[billwerk.credit]]
payment_method_type = "Discover"
[[billwerk.credit]]
payment_method_type = "CartesBancaires"
[[billwerk.credit]]
payment_method_type = "UnionPay"
[[billwerk.debit]]
payment_method_type = "Mastercard"
[[billwerk.debit]]
payment_method_type = "Visa"
[[billwerk.debit]]
payment_method_type = "Interac"
[[billwerk.debit]]
payment_method_type = "AmericanExpress"
[[billwerk.debit]]
payment_method_type = "JCB"
[[billwerk.debit]]
payment_method_type = "DinersClub"
[[billwerk.debit]]
payment_method_type = "Discover"
[[billwerk.debit]]
payment_method_type = "CartesBancaires"
[[billwerk.debit]]
payment_method_type = "UnionPay"
[billwerk.connector_auth.BodyKey]
api_key="Private Api Key"
key1="Public Api Key"

View File

@ -1913,4 +1913,45 @@ terminal_uuid="Terminal UUID"
pay_wall_secret="Pay Wall Secret" pay_wall_secret="Pay Wall Secret"
[zen.metadata.google_pay] [zen.metadata.google_pay]
terminal_uuid="Terminal UUID" terminal_uuid="Terminal UUID"
pay_wall_secret="Pay Wall Secret" pay_wall_secret="Pay Wall Secret"
[billwerk]
[[billwerk.credit]]
payment_method_type = "Mastercard"
[[billwerk.credit]]
payment_method_type = "Visa"
[[billwerk.credit]]
payment_method_type = "Interac"
[[billwerk.credit]]
payment_method_type = "AmericanExpress"
[[billwerk.credit]]
payment_method_type = "JCB"
[[billwerk.credit]]
payment_method_type = "DinersClub"
[[billwerk.credit]]
payment_method_type = "Discover"
[[billwerk.credit]]
payment_method_type = "CartesBancaires"
[[billwerk.credit]]
payment_method_type = "UnionPay"
[[billwerk.debit]]
payment_method_type = "Mastercard"
[[billwerk.debit]]
payment_method_type = "Visa"
[[billwerk.debit]]
payment_method_type = "Interac"
[[billwerk.debit]]
payment_method_type = "AmericanExpress"
[[billwerk.debit]]
payment_method_type = "JCB"
[[billwerk.debit]]
payment_method_type = "DinersClub"
[[billwerk.debit]]
payment_method_type = "Discover"
[[billwerk.debit]]
payment_method_type = "CartesBancaires"
[[billwerk.debit]]
payment_method_type = "UnionPay"
[billwerk.connector_auth.BodyKey]
api_key="Private Api Key"
key1="Public Api Key"

View File

@ -2589,4 +2589,45 @@ api_key="Api Key"
[threedsecureio.metadata] [threedsecureio.metadata]
mcc="MCC" mcc="MCC"
merchant_country_code="3 digit numeric country code" merchant_country_code="3 digit numeric country code"
merchant_name="Name of the merchant" merchant_name="Name of the merchant"
[billwerk]
[[billwerk.credit]]
payment_method_type = "Mastercard"
[[billwerk.credit]]
payment_method_type = "Visa"
[[billwerk.credit]]
payment_method_type = "Interac"
[[billwerk.credit]]
payment_method_type = "AmericanExpress"
[[billwerk.credit]]
payment_method_type = "JCB"
[[billwerk.credit]]
payment_method_type = "DinersClub"
[[billwerk.credit]]
payment_method_type = "Discover"
[[billwerk.credit]]
payment_method_type = "CartesBancaires"
[[billwerk.credit]]
payment_method_type = "UnionPay"
[[billwerk.debit]]
payment_method_type = "Mastercard"
[[billwerk.debit]]
payment_method_type = "Visa"
[[billwerk.debit]]
payment_method_type = "Interac"
[[billwerk.debit]]
payment_method_type = "AmericanExpress"
[[billwerk.debit]]
payment_method_type = "JCB"
[[billwerk.debit]]
payment_method_type = "DinersClub"
[[billwerk.debit]]
payment_method_type = "Discover"
[[billwerk.debit]]
payment_method_type = "CartesBancaires"
[[billwerk.debit]]
payment_method_type = "UnionPay"
[billwerk.connector_auth.BodyKey]
api_key="Private Api Key"
key1="Public Api Key"

View File

@ -2,12 +2,15 @@ pub mod transformers;
use std::fmt::Debug; use std::fmt::Debug;
use base64::Engine;
use error_stack::{report, ResultExt}; use error_stack::{report, ResultExt};
use masking::ExposeInterface; use masking::PeekInterface;
use transformers as billwerk; use transformers as billwerk;
use super::utils::RefundsRequestData;
use crate::{ use crate::{
configs::settings, configs::settings,
consts,
core::errors::{self, CustomResult}, core::errors::{self, CustomResult},
events::connector_api_logs::ConnectorEvent, events::connector_api_logs::ConnectorEvent,
headers, headers,
@ -40,16 +43,6 @@ impl api::RefundExecute for Billwerk {}
impl api::RefundSync for Billwerk {} impl api::RefundSync for Billwerk {}
impl api::PaymentToken for Billwerk {} impl api::PaymentToken for Billwerk {}
impl
ConnectorIntegration<
api::PaymentMethodToken,
types::PaymentMethodTokenizationData,
types::PaymentsResponseData,
> for Billwerk
{
// Not Implemented (R)
}
impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Billwerk impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Billwerk
where where
Self: ConnectorIntegration<Flow, Request, Response>, Self: ConnectorIntegration<Flow, Request, Response>,
@ -92,9 +85,10 @@ impl ConnectorCommon for Billwerk {
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> { ) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
let auth = billwerk::BillwerkAuthType::try_from(auth_type) let auth = billwerk::BillwerkAuthType::try_from(auth_type)
.change_context(errors::ConnectorError::FailedToObtainAuthType)?; .change_context(errors::ConnectorError::FailedToObtainAuthType)?;
let encoded_api_key = consts::BASE64_ENGINE.encode(format!("{}:", auth.api_key.peek()));
Ok(vec![( Ok(vec![(
headers::AUTHORIZATION.to_string(), headers::AUTHORIZATION.to_string(),
auth.api_key.expose().into_masked(), format!("Basic {encoded_api_key}").into_masked(),
)]) )])
} }
@ -113,9 +107,13 @@ impl ConnectorCommon for Billwerk {
Ok(ErrorResponse { Ok(ErrorResponse {
status_code: res.status_code, status_code: res.status_code,
code: response.code, code: response
message: response.message, .code
reason: response.reason, .map_or(consts::NO_ERROR_CODE.to_string(), |code| code.to_string()),
message: response
.message
.unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
reason: Some(response.error),
attempt_status: None, attempt_status: None,
connector_transaction_id: None, connector_transaction_id: None,
}) })
@ -123,7 +121,20 @@ impl ConnectorCommon for Billwerk {
} }
impl ConnectorValidation for Billwerk { impl ConnectorValidation for Billwerk {
//TODO: implement functions when support enabled fn validate_capture_method(
&self,
capture_method: Option<common_enums::CaptureMethod>,
_pmt: Option<common_enums::PaymentMethodType>,
) -> CustomResult<(), errors::ConnectorError> {
let capture_method = capture_method.unwrap_or_default();
match capture_method {
common_enums::CaptureMethod::Automatic | common_enums::CaptureMethod::Manual => Ok(()),
common_enums::CaptureMethod::ManualMultiple
| common_enums::CaptureMethod::Scheduled => Err(
super::utils::construct_not_implemented_error_report(capture_method, self.id()),
),
}
}
} }
impl ConnectorIntegration<api::Session, types::PaymentsSessionData, types::PaymentsResponseData> impl ConnectorIntegration<api::Session, types::PaymentsSessionData, types::PaymentsResponseData>
@ -146,6 +157,105 @@ impl
{ {
} }
impl
ConnectorIntegration<
api::PaymentMethodToken,
types::PaymentMethodTokenizationData,
types::PaymentsResponseData,
> for Billwerk
{
fn get_headers(
&self,
req: &types::TokenizationRouterData,
connectors: &settings::Connectors,
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
self.build_headers(req, connectors)
}
fn get_content_type(&self) -> &'static str {
self.common_get_content_type()
}
fn get_url(
&self,
_req: &types::TokenizationRouterData,
connectors: &settings::Connectors,
) -> CustomResult<String, errors::ConnectorError> {
let base_url = connectors
.billwerk
.secondary_base_url
.as_ref()
.ok_or(errors::ConnectorError::FailedToObtainIntegrationUrl)?;
Ok(format!("{base_url}v1/token"))
}
fn get_request_body(
&self,
req: &types::TokenizationRouterData,
_connectors: &settings::Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> {
let connector_req = billwerk::BillwerkTokenRequest::try_from(req)?;
Ok(RequestContent::Json(Box::new(connector_req)))
}
fn build_request(
&self,
req: &types::TokenizationRouterData,
connectors: &settings::Connectors,
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
Ok(Some(
services::RequestBuilder::new()
.method(services::Method::Post)
.url(&types::TokenizationType::get_url(self, req, connectors)?)
.attach_default_headers()
.headers(types::TokenizationType::get_headers(self, req, connectors)?)
.set_body(types::TokenizationType::get_request_body(
self, req, connectors,
)?)
.build(),
))
}
fn handle_response(
&self,
data: &types::TokenizationRouterData,
event_builder: Option<&mut ConnectorEvent>,
res: Response,
) -> CustomResult<types::TokenizationRouterData, errors::ConnectorError>
where
types::PaymentsResponseData: Clone,
{
let response: billwerk::BillwerkTokenResponse = res
.response
.parse_struct("BillwerkTokenResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
event_builder.map(|i| i.set_response_body(&response));
router_env::logger::info!(connector_response=?response);
types::RouterData::try_from(types::ResponseRouterData {
response,
data: data.clone(),
http_code: res.status_code,
})
.change_context(errors::ConnectorError::ResponseHandlingFailed)
}
fn get_error_response(
&self,
res: Response,
event_builder: Option<&mut ConnectorEvent>,
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
self.build_error_response(res, event_builder)
}
fn get_5xx_error_response(
&self,
res: Response,
event_builder: Option<&mut ConnectorEvent>,
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
self.build_error_response(res, event_builder)
}
}
impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::PaymentsResponseData> impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::PaymentsResponseData>
for Billwerk for Billwerk
{ {
@ -164,9 +274,9 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
fn get_url( fn get_url(
&self, &self,
_req: &types::PaymentsAuthorizeRouterData, _req: &types::PaymentsAuthorizeRouterData,
_connectors: &settings::Connectors, connectors: &settings::Connectors,
) -> CustomResult<String, errors::ConnectorError> { ) -> CustomResult<String, errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) Ok(format!("{}v1/charge", self.base_url(connectors)))
} }
fn get_request_body( fn get_request_body(
@ -214,7 +324,7 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
) -> CustomResult<types::PaymentsAuthorizeRouterData, errors::ConnectorError> { ) -> CustomResult<types::PaymentsAuthorizeRouterData, errors::ConnectorError> {
let response: billwerk::BillwerkPaymentsResponse = res let response: billwerk::BillwerkPaymentsResponse = res
.response .response
.parse_struct("Billwerk PaymentsAuthorizeResponse") .parse_struct("Billwerk BillwerkPaymentsResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?; .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
event_builder.map(|i| i.set_response_body(&response)); event_builder.map(|i| i.set_response_body(&response));
router_env::logger::info!(connector_response=?response); router_env::logger::info!(connector_response=?response);
@ -232,6 +342,14 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
) -> CustomResult<ErrorResponse, errors::ConnectorError> { ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
self.build_error_response(res, event_builder) self.build_error_response(res, event_builder)
} }
fn get_5xx_error_response(
&self,
res: Response,
event_builder: Option<&mut ConnectorEvent>,
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
self.build_error_response(res, event_builder)
}
} }
impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsResponseData> impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsResponseData>
@ -251,10 +369,14 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe
fn get_url( fn get_url(
&self, &self,
_req: &types::PaymentsSyncRouterData, req: &types::PaymentsSyncRouterData,
_connectors: &settings::Connectors, connectors: &settings::Connectors,
) -> CustomResult<String, errors::ConnectorError> { ) -> CustomResult<String, errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) Ok(format!(
"{}v1/charge/{}",
self.base_url(connectors),
req.connector_request_reference_id
))
} }
fn build_request( fn build_request(
@ -280,7 +402,7 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe
) -> CustomResult<types::PaymentsSyncRouterData, errors::ConnectorError> { ) -> CustomResult<types::PaymentsSyncRouterData, errors::ConnectorError> {
let response: billwerk::BillwerkPaymentsResponse = res let response: billwerk::BillwerkPaymentsResponse = res
.response .response
.parse_struct("billwerk PaymentsSyncResponse") .parse_struct("billwerk BillwerkPaymentsResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?; .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
event_builder.map(|i| i.set_response_body(&response)); event_builder.map(|i| i.set_response_body(&response));
router_env::logger::info!(connector_response=?response); router_env::logger::info!(connector_response=?response);
@ -298,6 +420,14 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe
) -> CustomResult<ErrorResponse, errors::ConnectorError> { ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
self.build_error_response(res, event_builder) self.build_error_response(res, event_builder)
} }
fn get_5xx_error_response(
&self,
res: Response,
event_builder: Option<&mut ConnectorEvent>,
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
self.build_error_response(res, event_builder)
}
} }
impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::PaymentsResponseData> impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::PaymentsResponseData>
@ -317,18 +447,29 @@ impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::Payme
fn get_url( fn get_url(
&self, &self,
_req: &types::PaymentsCaptureRouterData, req: &types::PaymentsCaptureRouterData,
_connectors: &settings::Connectors, connectors: &settings::Connectors,
) -> CustomResult<String, errors::ConnectorError> { ) -> CustomResult<String, errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) let connector_transaction_id = &req.request.connector_transaction_id;
Ok(format!(
"{}v1/charge/{connector_transaction_id}/settle",
self.base_url(connectors)
))
} }
fn get_request_body( fn get_request_body(
&self, &self,
_req: &types::PaymentsCaptureRouterData, req: &types::PaymentsCaptureRouterData,
_connectors: &settings::Connectors, _connectors: &settings::Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> { ) -> CustomResult<RequestContent, errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into()) let connector_router_data = billwerk::BillwerkRouterData::try_from((
&self.get_currency_unit(),
req.request.currency,
req.request.amount_to_capture,
req,
))?;
let connector_req = billwerk::BillwerkCaptureRequest::try_from(&connector_router_data)?;
Ok(RequestContent::Json(Box::new(connector_req)))
} }
fn build_request( fn build_request(
@ -359,7 +500,7 @@ impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::Payme
) -> CustomResult<types::PaymentsCaptureRouterData, errors::ConnectorError> { ) -> CustomResult<types::PaymentsCaptureRouterData, errors::ConnectorError> {
let response: billwerk::BillwerkPaymentsResponse = res let response: billwerk::BillwerkPaymentsResponse = res
.response .response
.parse_struct("Billwerk PaymentsCaptureResponse") .parse_struct("Billwerk BillwerkPaymentsResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?; .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
event_builder.map(|i| i.set_response_body(&response)); event_builder.map(|i| i.set_response_body(&response));
router_env::logger::info!(connector_response=?response); router_env::logger::info!(connector_response=?response);
@ -377,11 +518,92 @@ impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::Payme
) -> CustomResult<ErrorResponse, errors::ConnectorError> { ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
self.build_error_response(res, event_builder) self.build_error_response(res, event_builder)
} }
fn get_5xx_error_response(
&self,
res: Response,
event_builder: Option<&mut ConnectorEvent>,
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
self.build_error_response(res, event_builder)
}
} }
impl ConnectorIntegration<api::Void, types::PaymentsCancelData, types::PaymentsResponseData> impl ConnectorIntegration<api::Void, types::PaymentsCancelData, types::PaymentsResponseData>
for Billwerk for Billwerk
{ {
fn get_headers(
&self,
req: &types::PaymentsCancelRouterData,
connectors: &settings::Connectors,
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
self.build_headers(req, connectors)
}
fn get_content_type(&self) -> &'static str {
self.common_get_content_type()
}
fn get_url(
&self,
req: &types::PaymentsCancelRouterData,
connectors: &settings::Connectors,
) -> CustomResult<String, errors::ConnectorError> {
let connector_transaction_id = &req.request.connector_transaction_id;
Ok(format!(
"{}v1/charge/{connector_transaction_id}/cancel",
self.base_url(connectors)
))
}
fn build_request(
&self,
req: &types::PaymentsCancelRouterData,
connectors: &settings::Connectors,
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
Ok(Some(
services::RequestBuilder::new()
.method(services::Method::Post)
.url(&types::PaymentsVoidType::get_url(self, req, connectors)?)
.attach_default_headers()
.headers(types::PaymentsVoidType::get_headers(self, req, connectors)?)
.build(),
))
}
fn handle_response(
&self,
data: &types::PaymentsCancelRouterData,
event_builder: Option<&mut ConnectorEvent>,
res: Response,
) -> CustomResult<types::PaymentsCancelRouterData, errors::ConnectorError> {
let response: billwerk::BillwerkPaymentsResponse = res
.response
.parse_struct("Billwerk BillwerkPaymentsResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
event_builder.map(|i| i.set_response_body(&response));
router_env::logger::info!(connector_response=?response);
types::RouterData::try_from(types::ResponseRouterData {
response,
data: data.clone(),
http_code: res.status_code,
})
}
fn get_error_response(
&self,
res: Response,
event_builder: Option<&mut ConnectorEvent>,
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
self.build_error_response(res, event_builder)
}
fn get_5xx_error_response(
&self,
res: Response,
event_builder: Option<&mut ConnectorEvent>,
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
self.build_error_response(res, event_builder)
}
} }
impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsResponseData> impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsResponseData>
@ -402,9 +624,9 @@ impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsRespon
fn get_url( fn get_url(
&self, &self,
_req: &types::RefundsRouterData<api::Execute>, _req: &types::RefundsRouterData<api::Execute>,
_connectors: &settings::Connectors, connectors: &settings::Connectors,
) -> CustomResult<String, errors::ConnectorError> { ) -> CustomResult<String, errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) Ok(format!("{}v1/refund", self.base_url(connectors)))
} }
fn get_request_body( fn get_request_body(
@ -467,6 +689,14 @@ impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsRespon
) -> CustomResult<ErrorResponse, errors::ConnectorError> { ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
self.build_error_response(res, event_builder) self.build_error_response(res, event_builder)
} }
fn get_5xx_error_response(
&self,
res: Response,
event_builder: Option<&mut ConnectorEvent>,
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
self.build_error_response(res, event_builder)
}
} }
impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponseData> for Billwerk { impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponseData> for Billwerk {
@ -484,10 +714,14 @@ impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponse
fn get_url( fn get_url(
&self, &self,
_req: &types::RefundSyncRouterData, req: &types::RefundSyncRouterData,
_connectors: &settings::Connectors, connectors: &settings::Connectors,
) -> CustomResult<String, errors::ConnectorError> { ) -> CustomResult<String, errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) let refund_id = req.request.get_connector_refund_id()?;
Ok(format!(
"{}v1/refund/{refund_id}",
self.base_url(connectors)
))
} }
fn build_request( fn build_request(
@ -534,6 +768,14 @@ impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponse
) -> CustomResult<ErrorResponse, errors::ConnectorError> { ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
self.build_error_response(res, event_builder) self.build_error_response(res, event_builder)
} }
fn get_5xx_error_response(
&self,
res: Response,
event_builder: Option<&mut ConnectorEvent>,
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
self.build_error_response(res, event_builder)
}
} }
#[async_trait::async_trait] #[async_trait::async_trait]

View File

@ -1,15 +1,16 @@
use masking::Secret; use common_utils::pii::{Email, SecretSerdeValue};
use masking::{ExposeInterface, Secret};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
connector::utils::PaymentsAuthorizeRequestData, connector::utils::{self, CardData, PaymentsAuthorizeRequestData, RouterData},
consts,
core::errors, core::errors,
types::{self, api, storage::enums}, types::{self, api, storage::enums},
}; };
//TODO: Fill the struct with respective fields
pub struct BillwerkRouterData<T> { pub struct BillwerkRouterData<T> {
pub amount: i64, // The type of amount that a connector accepts, for example, String, i64, f64, etc. pub amount: i64,
pub router_data: T, pub router_data: T,
} }
@ -30,7 +31,6 @@ impl<T>
T, T,
), ),
) -> Result<Self, Self::Error> { ) -> Result<Self, Self::Error> {
//Todo : use utils to convert the amount to the type of amount that a connector accepts
Ok(Self { Ok(Self {
amount, amount,
router_data: item, router_data: item,
@ -38,20 +38,144 @@ impl<T>
} }
} }
//TODO: Fill the struct with respective fields pub struct BillwerkAuthType {
#[derive(Default, Debug, Serialize, Eq, PartialEq)] pub(super) api_key: Secret<String>,
pub struct BillwerkPaymentsRequest { pub(super) public_api_key: Secret<String>,
amount: i64,
card: BillwerkCard,
} }
#[derive(Default, Debug, Serialize, Eq, PartialEq)] impl TryFrom<&types::ConnectorAuthType> for BillwerkAuthType {
pub struct BillwerkCard { type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(auth_type: &types::ConnectorAuthType) -> Result<Self, Self::Error> {
match auth_type {
types::ConnectorAuthType::BodyKey { api_key, key1 } => Ok(Self {
api_key: api_key.to_owned(),
public_api_key: key1.to_owned(),
}),
_ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
}
}
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum BillwerkTokenRequestIntent {
ChargeAndStore,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum BillwerkStrongAuthRule {
UseScaIfAvailableAuth,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BillwerkTokenRequest {
number: cards::CardNumber, number: cards::CardNumber,
expiry_month: Secret<String>, month: Secret<String>,
expiry_year: Secret<String>, year: Secret<String>,
cvc: Secret<String>, cvv: Secret<String>,
complete: bool, pkey: Secret<String>,
recurring: Option<bool>,
intent: Option<BillwerkTokenRequestIntent>,
strong_authentication_rule: Option<BillwerkStrongAuthRule>,
}
impl TryFrom<&types::TokenizationRouterData> for BillwerkTokenRequest {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(item: &types::TokenizationRouterData) -> Result<Self, Self::Error> {
match item.request.payment_method_data.clone() {
api::PaymentMethodData::Card(ccard) => {
let connector_auth = &item.connector_auth_type;
let auth_type = BillwerkAuthType::try_from(connector_auth)?;
Ok(Self {
number: ccard.card_number.clone(),
month: ccard.card_exp_month.clone(),
year: ccard.get_card_expiry_year_2_digit()?,
cvv: ccard.card_cvc,
pkey: auth_type.public_api_key,
recurring: None,
intent: None,
strong_authentication_rule: None,
})
}
api_models::payments::PaymentMethodData::Wallet(_)
| api_models::payments::PaymentMethodData::CardRedirect(_)
| api_models::payments::PaymentMethodData::PayLater(_)
| api_models::payments::PaymentMethodData::BankRedirect(_)
| api_models::payments::PaymentMethodData::BankDebit(_)
| api_models::payments::PaymentMethodData::BankTransfer(_)
| api_models::payments::PaymentMethodData::Crypto(_)
| api_models::payments::PaymentMethodData::MandatePayment
| api_models::payments::PaymentMethodData::Reward
| api_models::payments::PaymentMethodData::Upi(_)
| api_models::payments::PaymentMethodData::Voucher(_)
| api_models::payments::PaymentMethodData::GiftCard(_)
| api_models::payments::PaymentMethodData::CardToken(_) => {
Err(errors::ConnectorError::NotImplemented(
utils::get_unimplemented_payment_method_error_message("billwerk"),
)
.into())
}
}
}
}
#[derive(Debug, Deserialize, Serialize)]
pub struct BillwerkTokenResponse {
id: Secret<String>,
recurring: Option<bool>,
}
impl<T>
TryFrom<
types::ResponseRouterData<
api::PaymentMethodToken,
BillwerkTokenResponse,
T,
types::PaymentsResponseData,
>,
> for types::RouterData<api::PaymentMethodToken, T, types::PaymentsResponseData>
{
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
item: types::ResponseRouterData<
api::PaymentMethodToken,
BillwerkTokenResponse,
T,
types::PaymentsResponseData,
>,
) -> Result<Self, Self::Error> {
Ok(Self {
response: Ok(types::PaymentsResponseData::TokenizationResponse {
token: item.response.id.expose(),
}),
..item.data
})
}
}
#[derive(Debug, Serialize)]
pub struct BillwerkCustomerObject {
handle: Option<String>,
email: Option<Email>,
address: Option<Secret<String>>,
address2: Option<Secret<String>>,
city: Option<String>,
country: Option<common_enums::CountryAlpha2>,
first_name: Option<Secret<String>>,
last_name: Option<Secret<String>>,
}
#[derive(Debug, Serialize)]
pub struct BillwerkPaymentsRequest {
handle: String,
amount: i64,
source: Secret<String>,
currency: common_enums::Currency,
customer: BillwerkCustomerObject,
metadata: Option<SecretSerdeValue>,
settle: bool,
} }
impl TryFrom<&BillwerkRouterData<&types::PaymentsAuthorizeRouterData>> for BillwerkPaymentsRequest { impl TryFrom<&BillwerkRouterData<&types::PaymentsAuthorizeRouterData>> for BillwerkPaymentsRequest {
@ -59,68 +183,68 @@ impl TryFrom<&BillwerkRouterData<&types::PaymentsAuthorizeRouterData>> for Billw
fn try_from( fn try_from(
item: &BillwerkRouterData<&types::PaymentsAuthorizeRouterData>, item: &BillwerkRouterData<&types::PaymentsAuthorizeRouterData>,
) -> Result<Self, Self::Error> { ) -> Result<Self, Self::Error> {
match item.router_data.request.payment_method_data.clone() { if item.router_data.is_three_ds() {
api::PaymentMethodData::Card(req_card) => { return Err(errors::ConnectorError::NotImplemented(
let card = BillwerkCard { "Three_ds payments through Billwerk".to_string(),
number: req_card.card_number, )
expiry_month: req_card.card_exp_month, .into());
expiry_year: req_card.card_exp_year, };
cvc: req_card.card_cvc, let source = match item.router_data.get_payment_method_token()? {
complete: item.router_data.request.is_auto_capture()?, types::PaymentMethodToken::Token(pm_token) => Ok(Secret::new(pm_token)),
}; _ => Err(errors::ConnectorError::MissingRequiredField {
Ok(Self { field_name: "payment_method_token",
amount: item.amount.to_owned(),
card,
})
}
_ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()),
}
}
}
//TODO: Fill the struct with respective fields
// Auth Struct
pub struct BillwerkAuthType {
pub(super) api_key: Secret<String>,
}
impl TryFrom<&types::ConnectorAuthType> for BillwerkAuthType {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(auth_type: &types::ConnectorAuthType) -> Result<Self, Self::Error> {
match auth_type {
types::ConnectorAuthType::HeaderKey { api_key } => Ok(Self {
api_key: api_key.to_owned(),
}), }),
_ => Err(errors::ConnectorError::FailedToObtainAuthType.into()), }?;
} Ok(Self {
handle: item.router_data.connector_request_reference_id.clone(),
amount: item.amount,
source,
currency: item.router_data.request.currency,
customer: BillwerkCustomerObject {
handle: item.router_data.customer_id.clone(),
email: item.router_data.request.email.clone(),
address: item.router_data.get_optional_billing_line1(),
address2: item.router_data.get_optional_billing_line2(),
city: item.router_data.get_optional_billing_city(),
country: item.router_data.get_optional_billing_country(),
first_name: item.router_data.get_optional_billing_first_name(),
last_name: item.router_data.get_optional_billing_last_name(),
},
metadata: item.router_data.request.metadata.clone(),
settle: item.router_data.request.is_auto_capture()?,
})
} }
} }
// PaymentsResponse
//TODO: Append the remaining status flags #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
pub enum BillwerkPaymentStatus { pub enum BillwerkPaymentState {
Succeeded, Created,
Authorized,
Pending,
Settled,
Failed, Failed,
#[default] Cancelled,
Processing,
} }
impl From<BillwerkPaymentStatus> for enums::AttemptStatus { impl From<BillwerkPaymentState> for enums::AttemptStatus {
fn from(item: BillwerkPaymentStatus) -> Self { fn from(item: BillwerkPaymentState) -> Self {
match item { match item {
BillwerkPaymentStatus::Succeeded => Self::Charged, BillwerkPaymentState::Created | BillwerkPaymentState::Pending => Self::Pending,
BillwerkPaymentStatus::Failed => Self::Failure, BillwerkPaymentState::Authorized => Self::Authorized,
BillwerkPaymentStatus::Processing => Self::Authorizing, BillwerkPaymentState::Settled => Self::Charged,
BillwerkPaymentState::Failed => Self::Failure,
BillwerkPaymentState::Cancelled => Self::Voided,
} }
} }
} }
//TODO: Fill the struct with respective fields #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct BillwerkPaymentsResponse { pub struct BillwerkPaymentsResponse {
status: BillwerkPaymentStatus, state: BillwerkPaymentState,
id: String, handle: String,
error: Option<String>,
error_state: Option<String>,
} }
impl<F, T> impl<F, T>
@ -136,28 +260,65 @@ impl<F, T>
types::PaymentsResponseData, types::PaymentsResponseData,
>, >,
) -> Result<Self, Self::Error> { ) -> Result<Self, Self::Error> {
let error_response = if item.response.error.is_some() || item.response.error_state.is_some()
{
Some(types::ErrorResponse {
code: item
.response
.error_state
.clone()
.unwrap_or(consts::NO_ERROR_CODE.to_string()),
message: item
.response
.error_state
.unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
reason: item.response.error,
status_code: item.http_code,
attempt_status: None,
connector_transaction_id: Some(item.response.handle.clone()),
})
} else {
None
};
let payments_response = types::PaymentsResponseData::TransactionResponse {
resource_id: types::ResponseId::ConnectorTransactionId(item.response.handle.clone()),
redirection_data: None,
mandate_reference: None,
connector_metadata: None,
network_txn_id: None,
connector_response_reference_id: Some(item.response.handle),
incremental_authorization_allowed: None,
};
Ok(Self { Ok(Self {
status: enums::AttemptStatus::from(item.response.status), status: enums::AttemptStatus::from(item.response.state),
response: Ok(types::PaymentsResponseData::TransactionResponse { response: error_response.map_or_else(|| Ok(payments_response), Err),
resource_id: types::ResponseId::ConnectorTransactionId(item.response.id),
redirection_data: None,
mandate_reference: None,
connector_metadata: None,
network_txn_id: None,
connector_response_reference_id: None,
incremental_authorization_allowed: None,
}),
..item.data ..item.data
}) })
} }
} }
//TODO: Fill the struct with respective fields #[derive(Debug, Serialize)]
// REFUND : pub struct BillwerkCaptureRequest {
amount: i64,
}
impl TryFrom<&BillwerkRouterData<&types::PaymentsCaptureRouterData>> for BillwerkCaptureRequest {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
item: &BillwerkRouterData<&types::PaymentsCaptureRouterData>,
) -> Result<Self, Self::Error> {
Ok(Self {
amount: item.amount,
})
}
}
// Type definition for RefundRequest // Type definition for RefundRequest
#[derive(Default, Debug, Serialize)] #[derive(Debug, Serialize)]
pub struct BillwerkRefundRequest { pub struct BillwerkRefundRequest {
pub invoice: String,
pub amount: i64, pub amount: i64,
pub text: Option<String>,
} }
impl<F> TryFrom<&BillwerkRouterData<&types::RefundsRouterData<F>>> for BillwerkRefundRequest { impl<F> TryFrom<&BillwerkRouterData<&types::RefundsRouterData<F>>> for BillwerkRefundRequest {
@ -166,38 +327,36 @@ impl<F> TryFrom<&BillwerkRouterData<&types::RefundsRouterData<F>>> for BillwerkR
item: &BillwerkRouterData<&types::RefundsRouterData<F>>, item: &BillwerkRouterData<&types::RefundsRouterData<F>>,
) -> Result<Self, Self::Error> { ) -> Result<Self, Self::Error> {
Ok(Self { Ok(Self {
amount: item.amount.to_owned(), amount: item.amount,
invoice: item.router_data.request.connector_transaction_id.clone(),
text: item.router_data.request.reason.clone(),
}) })
} }
} }
// Type definition for Refund Response // Type definition for Refund Response
#[derive(Debug, Serialize, Deserialize)]
#[allow(dead_code)] #[serde(rename_all = "lowercase")]
#[derive(Debug, Serialize, Default, Deserialize, Clone)] pub enum RefundState {
pub enum RefundStatus { Refunded,
Succeeded,
Failed, Failed,
#[default]
Processing, Processing,
} }
impl From<RefundStatus> for enums::RefundStatus { impl From<RefundState> for enums::RefundStatus {
fn from(item: RefundStatus) -> Self { fn from(item: RefundState) -> Self {
match item { match item {
RefundStatus::Succeeded => Self::Success, RefundState::Refunded => Self::Success,
RefundStatus::Failed => Self::Failure, RefundState::Failed => Self::Failure,
RefundStatus::Processing => Self::Pending, RefundState::Processing => Self::Pending,
//TODO: Review mapping
} }
} }
} }
//TODO: Fill the struct with respective fields #[derive(Debug, Serialize, Deserialize)]
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct RefundResponse { pub struct RefundResponse {
id: String, id: String,
status: RefundStatus, state: RefundState,
} }
impl TryFrom<types::RefundsResponseRouterData<api::Execute, RefundResponse>> impl TryFrom<types::RefundsResponseRouterData<api::Execute, RefundResponse>>
@ -210,7 +369,7 @@ impl TryFrom<types::RefundsResponseRouterData<api::Execute, RefundResponse>>
Ok(Self { Ok(Self {
response: Ok(types::RefundsResponseData { response: Ok(types::RefundsResponseData {
connector_refund_id: item.response.id.to_string(), connector_refund_id: item.response.id.to_string(),
refund_status: enums::RefundStatus::from(item.response.status), refund_status: enums::RefundStatus::from(item.response.state),
}), }),
..item.data ..item.data
}) })
@ -227,18 +386,16 @@ impl TryFrom<types::RefundsResponseRouterData<api::RSync, RefundResponse>>
Ok(Self { Ok(Self {
response: Ok(types::RefundsResponseData { response: Ok(types::RefundsResponseData {
connector_refund_id: item.response.id.to_string(), connector_refund_id: item.response.id.to_string(),
refund_status: enums::RefundStatus::from(item.response.status), refund_status: enums::RefundStatus::from(item.response.state),
}), }),
..item.data ..item.data
}) })
} }
} }
//TODO: Fill the struct with respective fields #[derive(Debug, Serialize, Deserialize)]
#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
pub struct BillwerkErrorResponse { pub struct BillwerkErrorResponse {
pub status_code: u16, pub code: Option<i32>,
pub code: String, pub error: String,
pub message: String, pub message: Option<String>,
pub reason: Option<String>,
} }

View File

@ -88,6 +88,14 @@ pub trait RouterData {
fn get_optional_billing(&self) -> Option<&api::Address>; fn get_optional_billing(&self) -> Option<&api::Address>;
fn get_optional_shipping(&self) -> Option<&api::Address>; fn get_optional_shipping(&self) -> Option<&api::Address>;
fn get_optional_billing_line1(&self) -> Option<Secret<String>>;
fn get_optional_billing_line2(&self) -> Option<Secret<String>>;
fn get_optional_billing_city(&self) -> Option<String>;
fn get_optional_billing_country(&self) -> Option<enums::CountryAlpha2>;
fn get_optional_billing_zip(&self) -> Option<Secret<String>>;
fn get_optional_billing_state(&self) -> Option<Secret<String>>;
fn get_optional_billing_first_name(&self) -> Option<Secret<String>>;
fn get_optional_billing_last_name(&self) -> Option<Secret<String>>;
} }
pub trait PaymentResponseRouterData { pub trait PaymentResponseRouterData {
@ -202,6 +210,94 @@ impl<Flow, Request, Response> RouterData for types::RouterData<Flow, Request, Re
.ok_or_else(missing_field_err("session_token")) .ok_or_else(missing_field_err("session_token"))
} }
fn get_optional_billing_line1(&self) -> Option<Secret<String>> {
self.address
.get_payment_method_billing()
.and_then(|billing_address| {
billing_address
.clone()
.address
.and_then(|billing_address_details| billing_address_details.line1)
})
}
fn get_optional_billing_line2(&self) -> Option<Secret<String>> {
self.address
.get_payment_method_billing()
.and_then(|billing_address| {
billing_address
.clone()
.address
.and_then(|billing_address_details| billing_address_details.line2)
})
}
fn get_optional_billing_city(&self) -> Option<String> {
self.address
.get_payment_method_billing()
.and_then(|billing_address| {
billing_address
.clone()
.address
.and_then(|billing_address_details| billing_address_details.city)
})
}
fn get_optional_billing_country(&self) -> Option<enums::CountryAlpha2> {
self.address
.get_payment_method_billing()
.and_then(|billing_address| {
billing_address
.clone()
.address
.and_then(|billing_address_details| billing_address_details.country)
})
}
fn get_optional_billing_zip(&self) -> Option<Secret<String>> {
self.address
.get_payment_method_billing()
.and_then(|billing_address| {
billing_address
.clone()
.address
.and_then(|billing_address_details| billing_address_details.zip)
})
}
fn get_optional_billing_state(&self) -> Option<Secret<String>> {
self.address
.get_payment_method_billing()
.and_then(|billing_address| {
billing_address
.clone()
.address
.and_then(|billing_address_details| billing_address_details.state)
})
}
fn get_optional_billing_first_name(&self) -> Option<Secret<String>> {
self.address
.get_payment_method_billing()
.and_then(|billing_address| {
billing_address
.clone()
.address
.and_then(|billing_address_details| billing_address_details.first_name)
})
}
fn get_optional_billing_last_name(&self) -> Option<Secret<String>> {
self.address
.get_payment_method_billing()
.and_then(|billing_address| {
billing_address
.clone()
.address
.and_then(|billing_address_details| billing_address_details.last_name)
})
}
fn to_connector_meta<T>(&self) -> Result<T, Error> fn to_connector_meta<T>(&self) -> Result<T, Error>
where where
T: serde::de::DeserializeOwned, T: serde::de::DeserializeOwned,

View File

@ -1731,10 +1731,10 @@ pub(crate) fn validate_auth_and_metadata_type(
bankofamerica::transformers::BankOfAmericaAuthType::try_from(val)?; bankofamerica::transformers::BankOfAmericaAuthType::try_from(val)?;
Ok(()) Ok(())
} }
// api_enums::Connector::Billwerk => { api_enums::Connector::Billwerk => {
// billwerk::transformers::BillwerkAuthType::try_from(val)?; billwerk::transformers::BillwerkAuthType::try_from(val)?;
// Ok(()) Ok(())
// } Added as template code for future usage }
api_enums::Connector::Bitpay => { api_enums::Connector::Bitpay => {
bitpay::transformers::BitpayAuthType::try_from(val)?; bitpay::transformers::BitpayAuthType::try_from(val)?;
Ok(()) Ok(())

View File

@ -326,7 +326,7 @@ impl ConnectorData {
enums::Connector::Authorizedotnet => Ok(Box::new(&connector::Authorizedotnet)), enums::Connector::Authorizedotnet => Ok(Box::new(&connector::Authorizedotnet)),
enums::Connector::Bambora => Ok(Box::new(&connector::Bambora)), enums::Connector::Bambora => Ok(Box::new(&connector::Bambora)),
enums::Connector::Bankofamerica => Ok(Box::new(&connector::Bankofamerica)), enums::Connector::Bankofamerica => Ok(Box::new(&connector::Bankofamerica)),
// enums::Connector::Billwerk => Ok(Box::new(&connector::Billwerk)), Added as template code for future usage enums::Connector::Billwerk => Ok(Box::new(&connector::Billwerk)),
enums::Connector::Bitpay => Ok(Box::new(&connector::Bitpay)), enums::Connector::Bitpay => Ok(Box::new(&connector::Bitpay)),
enums::Connector::Bluesnap => Ok(Box::new(&connector::Bluesnap)), enums::Connector::Bluesnap => Ok(Box::new(&connector::Bluesnap)),
enums::Connector::Boku => Ok(Box::new(&connector::Boku)), enums::Connector::Boku => Ok(Box::new(&connector::Boku)),

View File

@ -188,7 +188,7 @@ impl ForeignTryFrom<api_enums::Connector> for common_enums::RoutableConnectors {
api_enums::Connector::Authorizedotnet => Self::Authorizedotnet, api_enums::Connector::Authorizedotnet => Self::Authorizedotnet,
api_enums::Connector::Bambora => Self::Bambora, api_enums::Connector::Bambora => Self::Bambora,
api_enums::Connector::Bankofamerica => Self::Bankofamerica, api_enums::Connector::Bankofamerica => Self::Bankofamerica,
// api_enums::Connector::Billwerk => Self::Billwerk, Added as template code for future usage api_enums::Connector::Billwerk => Self::Billwerk,
api_enums::Connector::Bitpay => Self::Bitpay, api_enums::Connector::Bitpay => Self::Bitpay,
api_enums::Connector::Bluesnap => Self::Bluesnap, api_enums::Connector::Bluesnap => Self::Bluesnap,
api_enums::Connector::Boku => Self::Boku, api_enums::Connector::Boku => Self::Boku,

View File

@ -72,6 +72,7 @@ authorizedotnet.base_url = "https://apitest.authorize.net/xml/v1/request.api"
bambora.base_url = "https://api.na.bambora.com" bambora.base_url = "https://api.na.bambora.com"
bankofamerica.base_url = "https://apitest.merchant-services.bankofamerica.com/" bankofamerica.base_url = "https://apitest.merchant-services.bankofamerica.com/"
billwerk.base_url = "https://api.reepay.com/" billwerk.base_url = "https://api.reepay.com/"
billwerk.secondary_base_url = "https://card.reepay.com/"
bitpay.base_url = "https://test.bitpay.com" bitpay.base_url = "https://test.bitpay.com"
bluesnap.base_url = "https://sandbox.bluesnap.com/" bluesnap.base_url = "https://sandbox.bluesnap.com/"
bluesnap.secondary_base_url = "https://sandpay.bluesnap.com/" bluesnap.secondary_base_url = "https://sandpay.bluesnap.com/"
@ -219,6 +220,7 @@ checkout = { long_lived_token = false, payment_method = "wallet", apple_pay_pre_
mollie = {long_lived_token = false, payment_method = "card"} mollie = {long_lived_token = false, payment_method = "card"}
braintree = { long_lived_token = false, payment_method = "card" } braintree = { long_lived_token = false, payment_method = "card" }
gocardless = {long_lived_token = true, payment_method = "bank_debit"} gocardless = {long_lived_token = true, payment_method = "bank_debit"}
billwerk = {long_lived_token = false, payment_method = "card"}
[connector_customer] [connector_customer]
connector_list = "gocardless,stax,stripe" connector_list = "gocardless,stax,stripe"

View File

@ -7067,6 +7067,7 @@
"authorizedotnet", "authorizedotnet",
"bambora", "bambora",
"bankofamerica", "bankofamerica",
"billwerk",
"bitpay", "bitpay",
"bluesnap", "bluesnap",
"boku", "boku",
@ -16813,6 +16814,7 @@
"airwallex", "airwallex",
"authorizedotnet", "authorizedotnet",
"bankofamerica", "bankofamerica",
"billwerk",
"bitpay", "bitpay",
"bambora", "bambora",
"bluesnap", "bluesnap",