Skip to content

Commit 55e01c1

Browse files
committed
cards-xendit
1 parent 0fddb93 commit 55e01c1

File tree

6 files changed

+296
-97
lines changed

6 files changed

+296
-97
lines changed

crates/hyperswitch_connectors/src/connectors/xendit.rs

Lines changed: 50 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use common_utils::{
66
errors::CustomResult,
77
ext_traits::BytesExt,
88
request::{Method, Request, RequestBuilder, RequestContent},
9-
types::{AmountConvertor, MinorUnit, MinorUnitForConnector},
9+
types::{AmountConvertor, FloatMajorUnit, FloatMajorUnitForConnector},
1010
};
1111
use error_stack::{report, ResultExt};
1212
use hyperswitch_domain_models::{
@@ -41,7 +41,6 @@ use hyperswitch_interfaces::{
4141
};
4242
use masking::{Mask, PeekInterface};
4343
use transformers as xendit;
44-
use url::form_urlencoded;
4544

4645
use crate::{
4746
constants::headers,
@@ -51,13 +50,13 @@ use crate::{
5150

5251
#[derive(Clone)]
5352
pub struct Xendit {
54-
amount_converter: &'static (dyn AmountConvertor<Output = MinorUnit> + Sync),
53+
amount_converter: &'static (dyn AmountConvertor<Output = FloatMajorUnit> + Sync),
5554
}
5655

5756
impl Xendit {
5857
pub fn new() -> &'static Self {
5958
&Self {
60-
amount_converter: &MinorUnitForConnector,
59+
amount_converter: &FloatMajorUnitForConnector,
6160
}
6261
}
6362
}
@@ -90,9 +89,7 @@ where
9089
) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
9190
let mut header = vec![(
9291
headers::CONTENT_TYPE.to_string(),
93-
PaymentsAuthorizeType::get_content_type(self)
94-
.to_string()
95-
.into(),
92+
self.get_content_type().to_string().into(),
9693
)];
9794
let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
9895
header.append(&mut api_key);
@@ -157,7 +154,11 @@ impl ConnectorIntegration<Void, PaymentsCancelData, PaymentsResponseData> for Xe
157154
_req: &PaymentsCancelRouterData,
158155
_connectors: &Connectors,
159156
) -> CustomResult<Option<Request>, errors::ConnectorError> {
160-
Err(errors::ConnectorError::NotImplemented("Cancel/Void flow".to_string()).into())
157+
Err(errors::ConnectorError::NotSupported {
158+
message: "Cancel/Void flow".to_string(),
159+
connector: "Xendit",
160+
}
161+
.into())
161162
}
162163
}
163164

@@ -376,12 +377,27 @@ impl ConnectorIntegration<Capture, PaymentsCaptureData, PaymentsResponseData> fo
376377
req: &PaymentsCaptureRouterData,
377378
_connectors: &Connectors,
378379
) -> CustomResult<RequestContent, errors::ConnectorError> {
379-
let amount = utils::convert_amount(
380+
let amount_to_capture = utils::convert_amount(
380381
self.amount_converter,
381382
req.request.minor_amount_to_capture,
382383
req.request.currency,
383384
)?;
384-
let connector_router_data = xendit::XenditRouterData::from((amount, req));
385+
let authorized_amount = utils::convert_amount(
386+
self.amount_converter,
387+
req.request.minor_payment_amount,
388+
req.request.currency,
389+
)?;
390+
println!(
391+
"Amount to capture {:?} Authroized amount {:?} ",
392+
amount_to_capture, authorized_amount
393+
);
394+
if amount_to_capture != authorized_amount {
395+
return Err(report!(errors::ConnectorError::NotSupported {
396+
message: "Partial Capture".to_string(),
397+
connector: "Xendit"
398+
}));
399+
}
400+
let connector_router_data = xendit::XenditRouterData::from((amount_to_capture, req));
385401
let connector_req = xendit::XenditPaymentsCaptureRequest::try_from(connector_router_data)?;
386402
Ok(RequestContent::Json(Box::new(connector_req)))
387403
}
@@ -603,7 +619,7 @@ impl webhooks::IncomingWebhook for Xendit {
603619
&self,
604620
_request: &webhooks::IncomingWebhookRequestDetails<'_>,
605621
) -> CustomResult<api_models::webhooks::IncomingWebhookEvent, errors::ConnectorError> {
606-
Ok(api_models::webhooks::IncomingWebhookEvent::EventNotSupported)
622+
Err(report!(errors::ConnectorError::WebhooksNotImplemented))
607623
}
608624

609625
fn get_webhook_resource_object(
@@ -619,42 +635,33 @@ impl ConnectorSpecifications for Xendit {}
619635
impl ConnectorRedirectResponse for Xendit {
620636
fn get_flow_type(
621637
&self,
622-
query_params: &str,
638+
_query_params: &str,
623639
_json_payload: Option<serde_json::Value>,
624640
action: PaymentAction,
625641
) -> CustomResult<CallConnectorAction, errors::ConnectorError> {
626642
match action {
627-
PaymentAction::PSync | PaymentAction::PaymentAuthenticateCompleteAuthorize => {
628-
Ok(CallConnectorAction::Trigger)
629-
}
630-
PaymentAction::CompleteAuthorize => {
631-
let parsed_query: Vec<(String, String)> =
632-
form_urlencoded::parse(query_params.as_bytes())
633-
.into_owned()
634-
.collect();
635-
let status = parsed_query
636-
.iter()
637-
.find(|(key, _)| key == "status")
638-
.map(|(_, value)| value.as_str());
639-
640-
match status {
641-
Some("VERIFIED") => Ok(CallConnectorAction::StatusUpdate {
642-
status: enums::AttemptStatus::AuthenticationSuccessful,
643-
error_code: None,
644-
error_message: None,
645-
}),
646-
Some(unexpected_status) => Ok(CallConnectorAction::StatusUpdate {
647-
status: enums::AttemptStatus::AuthenticationFailed,
648-
error_code: Some("INVALID_STATUS".to_string()),
649-
error_message: Some(format!("Unexpected status: {}", unexpected_status)),
650-
}),
651-
None => Ok(CallConnectorAction::StatusUpdate {
652-
status: enums::AttemptStatus::AuthenticationFailed,
653-
error_code: Some("STATUS_NOT_FOUND".to_string()),
654-
error_message: Some("Status parameter is missing".to_string()),
655-
}),
656-
}
657-
}
643+
PaymentAction::PSync
644+
| PaymentAction::PaymentAuthenticateCompleteAuthorize
645+
| PaymentAction::CompleteAuthorize => Ok(CallConnectorAction::Trigger),
646+
// PaymentAction::CompleteAuthorize => {
647+
// let parsed_query: Vec<(String, String)> =
648+
// form_urlencoded::parse(query_params.as_bytes())
649+
// .into_owned()
650+
// .collect();
651+
// let status = parsed_query
652+
// .iter()
653+
// .find(|(key, _)| key == "status")
654+
// .map(|(_, value)| value.as_str());
655+
656+
// match status {
657+
// Some("VERIFIED") => Ok(CallConnectorAction::Trigger),
658+
// _ => Ok(CallConnectorAction::StatusUpdate {
659+
// status: enums::AttemptStatus::AuthenticationFailed,
660+
// error_code: Some("INVALID_STATUS".to_string()),
661+
// error_message: Some("INVALID_STATUS".to_string()),
662+
// }),
663+
// }
664+
// }
658665
}
659666
}
660667
}

crates/hyperswitch_connectors/src/connectors/xendit/transformers.rs

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::collections::HashMap;
22

33
use cards::CardNumber;
44
use common_enums::enums;
5-
use common_utils::{pii, request::Method, types::MinorUnit};
5+
use common_utils::{pii, request::Method, types::FloatMajorUnit};
66
use hyperswitch_domain_models::{
77
payment_method_data::PaymentMethodData,
88
router_data::{ConnectorAuthType, ErrorResponse, RouterData},
@@ -20,7 +20,7 @@ use hyperswitch_interfaces::{
2020
consts::{NO_ERROR_CODE, NO_ERROR_MESSAGE},
2121
errors,
2222
};
23-
use masking::{ExposeInterface, Secret};
23+
use masking::{ExposeInterface, PeekInterface, Secret};
2424
use serde::{Deserialize, Serialize};
2525

2626
use crate::{
@@ -33,7 +33,7 @@ use crate::{
3333

3434
//TODO: Fill the struct with respective fields
3535
pub struct XenditRouterData<T> {
36-
pub amount: MinorUnit, // The type of amount that a connector accepts, for example, String, i64, f64, etc.
36+
pub amount: FloatMajorUnit, // The type of amount that a connector accepts, for example, String, i64, f64, etc.
3737
pub router_data: T,
3838
}
3939

@@ -58,11 +58,11 @@ pub struct CardPaymentRequest {
5858
pub payment_type: PaymentMethodType,
5959
pub card: CardInfo,
6060
pub reusability: TransactionType,
61-
pub reference_id: String,
61+
pub reference_id: Secret<String>,
6262
}
6363
#[derive(Serialize, Deserialize, Debug)]
6464
pub struct MandatePaymentRequest {
65-
pub amount: MinorUnit,
65+
pub amount: FloatMajorUnit,
6666
pub currency: common_enums::Currency,
6767
pub capture_method: String,
6868
pub payment_method_id: Secret<String>,
@@ -74,11 +74,11 @@ pub struct XenditRedirectionResponse {
7474
}
7575
#[derive(Serialize, Deserialize, Debug)]
7676
pub struct XenditPaymentsCaptureRequest {
77-
pub capture_amount: MinorUnit,
77+
pub capture_amount: FloatMajorUnit,
7878
}
7979
#[derive(Serialize, Deserialize, Debug)]
8080
pub struct XenditPaymentsRequest {
81-
pub amount: MinorUnit,
81+
pub amount: FloatMajorUnit,
8282
pub currency: common_enums::Currency,
8383
pub capture_method: String,
8484
#[serde(skip_serializing_if = "Option::is_none")]
@@ -133,7 +133,7 @@ pub struct XenditPaymentResponse {
133133
pub actions: Option<Vec<Action>>,
134134
pub payment_method: PaymentMethodInfo,
135135
pub failure_code: Option<String>,
136-
pub reference_id: String,
136+
pub reference_id: Secret<String>,
137137
}
138138

139139
fn map_payment_response_to_attempt_status(
@@ -142,13 +142,14 @@ fn map_payment_response_to_attempt_status(
142142
) -> enums::AttemptStatus {
143143
match response.status {
144144
PaymentStatus::Failed => enums::AttemptStatus::Failure,
145-
PaymentStatus::Succeeded | PaymentStatus::Pending | PaymentStatus::Verified => {
145+
PaymentStatus::Succeeded | PaymentStatus::Verified => {
146146
if is_auto_capture {
147147
enums::AttemptStatus::Charged
148148
} else {
149149
enums::AttemptStatus::Authorized
150150
}
151151
}
152+
PaymentStatus::Pending => enums::AttemptStatus::Pending,
152153
PaymentStatus::RequiresAction => enums::AttemptStatus::AuthenticationPending,
153154
PaymentStatus::AwaitingCapture => enums::AttemptStatus::Authorized,
154155
}
@@ -182,17 +183,13 @@ impl TryFrom<XenditRouterData<&PaymentsAuthorizeRouterData>> for XenditPaymentsR
182183
amount: item.amount,
183184
payment_method: Some(PaymentMethod::Card(CardPaymentRequest {
184185
payment_type: PaymentMethodType::CARD,
185-
reference_id: item.router_data.connector_request_reference_id.clone(),
186+
reference_id: Secret::new(
187+
item.router_data.connector_request_reference_id.clone(),
188+
),
186189
card: CardInfo {
187190
channel_properties: ChannelProperties {
188-
success_return_url: item
189-
.router_data
190-
.request
191-
.get_complete_authorize_url()?,
192-
failure_return_url: item
193-
.router_data
194-
.request
195-
.get_complete_authorize_url()?,
191+
success_return_url: item.router_data.request.get_router_return_url()?,
192+
failure_return_url: item.router_data.request.get_router_return_url()?,
196193
skip_three_d_secure: !item.router_data.is_three_ds(),
197194
},
198195
card_information: CardInformation {
@@ -220,8 +217,8 @@ impl TryFrom<XenditRouterData<&PaymentsAuthorizeRouterData>> for XenditPaymentsR
220217
}),
221218
PaymentMethodData::MandatePayment => Ok(Self {
222219
channel_properties: Some(ChannelProperties {
223-
success_return_url: item.router_data.request.get_complete_authorize_url()?,
224-
failure_return_url: item.router_data.request.get_complete_authorize_url()?,
220+
success_return_url: item.router_data.request.get_router_return_url()?,
221+
failure_return_url: item.router_data.request.get_router_return_url()?,
225222
skip_three_d_secure: true,
226223
}),
227224
capture_method: match item.router_data.request.is_auto_capture()? {
@@ -291,7 +288,7 @@ impl<F>
291288
status_code: item.http_code,
292289
})
293290
} else {
294-
let x = Ok(PaymentsResponseData::TransactionResponse {
291+
Ok(PaymentsResponseData::TransactionResponse {
295292
resource_id: ResponseId::ConnectorTransactionId(item.response.id.clone()),
296293
redirection_data: match item.response.actions {
297294
Some(actions) if !actions.is_empty() => {
@@ -319,11 +316,12 @@ impl<F>
319316
},
320317
connector_metadata: None,
321318
network_txn_id: None,
322-
connector_response_reference_id: Some(item.response.reference_id),
319+
connector_response_reference_id: Some(
320+
item.response.reference_id.peek().to_string(),
321+
),
323322
incremental_authorization_allowed: None,
324323
charge_id: None,
325-
});
326-
x
324+
})
327325
};
328326
Ok(Self {
329327
status,
@@ -376,7 +374,9 @@ impl<F>
376374
mandate_reference: Box::new(None),
377375
connector_metadata: None,
378376
network_txn_id: None,
379-
connector_response_reference_id: Some(item.response.reference_id),
377+
connector_response_reference_id: Some(
378+
item.response.reference_id.peek().to_string(),
379+
),
380380
incremental_authorization_allowed: None,
381381
charge_id: None,
382382
})
@@ -420,7 +420,7 @@ impl TryFrom<PaymentsSyncResponseRouterData<XenditPaymentResponse>> for Payments
420420
})
421421
} else {
422422
Ok(PaymentsResponseData::TransactionResponse {
423-
resource_id: ResponseId::ConnectorTransactionId(item.response.id.clone()),
423+
resource_id: ResponseId::NoResponseId,
424424
redirection_data: Box::new(None),
425425
mandate_reference: Box::new(None),
426426
connector_metadata: None,
@@ -437,8 +437,8 @@ impl TryFrom<PaymentsSyncResponseRouterData<XenditPaymentResponse>> for Payments
437437
})
438438
}
439439
}
440-
impl<T> From<(MinorUnit, T)> for XenditRouterData<T> {
441-
fn from((amount, item): (MinorUnit, T)) -> Self {
440+
impl<T> From<(FloatMajorUnit, T)> for XenditRouterData<T> {
441+
fn from((amount, item): (FloatMajorUnit, T)) -> Self {
442442
Self {
443443
amount,
444444
router_data: item,
@@ -469,7 +469,7 @@ impl TryFrom<&ConnectorAuthType> for XenditAuthType {
469469
// Type definition for RefundRequest
470470
#[derive(Default, Debug, Serialize)]
471471
pub struct XenditRefundRequest {
472-
pub amount: MinorUnit,
472+
pub amount: FloatMajorUnit,
473473
pub payment_request_id: String,
474474
pub reason: String,
475475
}

crates/router/src/configs/defaults/payment_connector_required_fields.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3264,7 +3264,8 @@ impl Default for settings::RequiredFields {
32643264
enums::Connector::Xendit,
32653265
RequiredFieldFinal {
32663266
mandate: HashMap::new(),
3267-
non_mandate: HashMap::from(
3267+
non_mandate:HashMap::new(),
3268+
common: HashMap::from(
32683269
[
32693270
(
32703271
"payment_method_data.card.card_number".to_string(),
@@ -3323,7 +3324,6 @@ impl Default for settings::RequiredFields {
33233324

33243325
]
33253326
),
3326-
common: HashMap::new(),
33273327
}
33283328
),
33293329
(
@@ -6526,7 +6526,8 @@ impl Default for settings::RequiredFields {
65266526
enums::Connector::Xendit,
65276527
RequiredFieldFinal {
65286528
mandate: HashMap::new(),
6529-
non_mandate: HashMap::from(
6529+
non_mandate:HashMap::new(),
6530+
common: HashMap::from(
65306531
[
65316532
(
65326533
"payment_method_data.card.card_number".to_string(),
@@ -6585,7 +6586,6 @@ impl Default for settings::RequiredFields {
65856586

65866587
]
65876588
),
6588-
common: HashMap::new(),
65896589
}
65906590
),
65916591
(

0 commit comments

Comments
 (0)