mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-27 11:24:45 +08:00
feat(connector): Cybersource Authorize (#154)
Co-authored-by: Arun Raj M <jarnura47@gmail.com>
This commit is contained in:
@ -38,7 +38,7 @@ locker_decryption_key2 = ""
|
||||
|
||||
[connectors.supported]
|
||||
wallets = ["klarna","braintree","applepay"]
|
||||
cards = ["stripe","adyen","authorizedotnet","checkout","braintree","aci","shift4"]
|
||||
cards = ["stripe","adyen","authorizedotnet","checkout","braintree","aci","shift4","cybersource"]
|
||||
|
||||
[eph_key]
|
||||
validity = 1
|
||||
@ -67,6 +67,9 @@ base_url = "https://api-na.playground.klarna.com/"
|
||||
[connectors.applepay]
|
||||
base_url = "https://apple-pay-gateway.apple.com/"
|
||||
|
||||
[connectors.cybersource]
|
||||
base_url = "https://apitest.cybersource.com/"
|
||||
|
||||
[connectors.shift4]
|
||||
base_url = "https://api.shift4.com/"
|
||||
|
||||
|
||||
@ -119,14 +119,16 @@ base_url = "https://api-na.playground.klarna.com/"
|
||||
[connectors.applepay]
|
||||
base_url = "https://apple-pay-gateway.apple.com/"
|
||||
|
||||
[connectors.cybersource]
|
||||
base_url = "https://apitest.cybersource.com/"
|
||||
|
||||
[connectors.shift4]
|
||||
base_url = "https://api.shift4.com/"
|
||||
|
||||
# This data is used to call respective connectors for wallets and cards
|
||||
[connectors.supported]
|
||||
wallets = ["klarna","braintree","applepay"]
|
||||
cards = ["stripe","adyen","authorizedotnet","checkout","braintree"]
|
||||
|
||||
cards = ["stripe","adyen","authorizedotnet","checkout","braintree", "cybersource"]
|
||||
|
||||
# Scheduler settings provides a point to modify the behaviour of scheduler flow.
|
||||
# It defines the the streams/queues name and configuration as well as event selection variables
|
||||
|
||||
@ -74,9 +74,12 @@ base_url = "https://api-na.playground.klarna.com/"
|
||||
[connectors.applepay]
|
||||
base_url = "https://apple-pay-gateway.apple.com/"
|
||||
|
||||
[connectors.cybersource]
|
||||
base_url = "https://apitest.cybersource.com/"
|
||||
|
||||
[connectors.shift4]
|
||||
base_url = "https://api.shift4.com/"
|
||||
|
||||
[connectors.supported]
|
||||
wallets = ["klarna","braintree","applepay"]
|
||||
cards = ["stripe","adyen","authorizedotnet","checkout","braintree","shift4"]
|
||||
cards = ["stripe","adyen","authorizedotnet","checkout","braintree","shift4","cybersource"]
|
||||
|
||||
@ -491,6 +491,7 @@ pub enum Connector {
|
||||
Authorizedotnet,
|
||||
Braintree,
|
||||
Checkout,
|
||||
Cybersource,
|
||||
#[default]
|
||||
Dummy,
|
||||
Klarna,
|
||||
|
||||
@ -57,5 +57,5 @@ num_partitions = 64
|
||||
max_read_count = 100
|
||||
|
||||
[connectors.supported]
|
||||
wallets = ["klarna", "braintree"]
|
||||
cards = ["stripe", "adyen", "authorizedotnet", "checkout", "braintree"]
|
||||
wallets = ["klarna","braintree"]
|
||||
cards = ["stripe","adyen","authorizedotnet","checkout","braintree", "cybersource"]
|
||||
|
||||
@ -113,6 +113,7 @@ pub struct Connectors {
|
||||
pub braintree: ConnectorParams,
|
||||
pub checkout: ConnectorParams,
|
||||
pub klarna: ConnectorParams,
|
||||
pub cybersource: ConnectorParams,
|
||||
pub shift4: ConnectorParams,
|
||||
pub stripe: ConnectorParams,
|
||||
pub supported: SupportedConnectors,
|
||||
|
||||
@ -4,6 +4,7 @@ pub mod applepay;
|
||||
pub mod authorizedotnet;
|
||||
pub mod braintree;
|
||||
pub mod checkout;
|
||||
pub mod cybersource;
|
||||
pub mod klarna;
|
||||
pub mod stripe;
|
||||
|
||||
@ -11,5 +12,6 @@ pub mod shift4;
|
||||
|
||||
pub use self::{
|
||||
aci::Aci, adyen::Adyen, applepay::Applepay, authorizedotnet::Authorizedotnet,
|
||||
braintree::Braintree, checkout::Checkout, klarna::Klarna, shift4::Shift4, stripe::Stripe,
|
||||
braintree::Braintree, checkout::Checkout, cybersource::Cybersource, klarna::Klarna,
|
||||
shift4::Shift4, stripe::Stripe,
|
||||
};
|
||||
|
||||
303
crates/router/src/connector/cybersource.rs
Normal file
303
crates/router/src/connector/cybersource.rs
Normal file
@ -0,0 +1,303 @@
|
||||
mod transformers;
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
use base64;
|
||||
use bytes::Bytes;
|
||||
use error_stack::{IntoReport, ResultExt};
|
||||
use ring::{digest, hmac};
|
||||
use time::OffsetDateTime;
|
||||
use transformers as cybersource;
|
||||
use url::Url;
|
||||
|
||||
use crate::{
|
||||
configs::settings,
|
||||
consts,
|
||||
core::errors::{self, CustomResult},
|
||||
headers, logger, services,
|
||||
types::{self, api},
|
||||
utils::{self, BytesExt},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Cybersource;
|
||||
|
||||
impl Cybersource {
|
||||
pub fn generate_digest(&self, payload: &[u8]) -> String {
|
||||
let payload_digest = digest::digest(&digest::SHA256, payload);
|
||||
base64::encode(payload_digest)
|
||||
}
|
||||
|
||||
pub fn generate_signature(
|
||||
&self,
|
||||
auth: cybersource::CybersourceAuthType,
|
||||
host: String,
|
||||
resource: &str,
|
||||
payload: &str,
|
||||
date: OffsetDateTime,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
let cybersource::CybersourceAuthType {
|
||||
api_key,
|
||||
merchant_account,
|
||||
api_secret,
|
||||
} = auth;
|
||||
|
||||
let headers_for_post_method = "host date (request-target) digest v-c-merchant-id";
|
||||
let signature_string = format!(
|
||||
"host: {host}\n\
|
||||
date: {date}\n\
|
||||
(request-target): post {resource}\n\
|
||||
digest: SHA-256={}\n\
|
||||
v-c-merchant-id: {merchant_account}",
|
||||
self.generate_digest(payload.as_bytes())
|
||||
);
|
||||
let key_value = base64::decode(api_secret)
|
||||
.into_report()
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
let key = hmac::Key::new(hmac::HMAC_SHA256, &key_value);
|
||||
let signature_value =
|
||||
base64::encode(hmac::sign(&key, signature_string.as_bytes()).as_ref());
|
||||
let signature_header = format!(
|
||||
r#"keyid="{api_key}", algorithm="HmacSHA256", headers="{headers_for_post_method}", signature="{signature_value}""#
|
||||
);
|
||||
|
||||
Ok(signature_header)
|
||||
}
|
||||
}
|
||||
|
||||
impl api::ConnectorCommon for Cybersource {
|
||||
fn id(&self) -> &'static str {
|
||||
"cybersource"
|
||||
}
|
||||
|
||||
fn common_get_content_type(&self) -> &'static str {
|
||||
"application/json"
|
||||
}
|
||||
|
||||
fn base_url<'a>(&self, connectors: &'a settings::Connectors) -> &'a str {
|
||||
connectors.cybersource.base_url.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl api::Payment for Cybersource {}
|
||||
impl api::PaymentAuthorize for Cybersource {}
|
||||
impl api::PaymentSync for Cybersource {}
|
||||
impl api::PaymentVoid for Cybersource {}
|
||||
impl api::PaymentCapture for Cybersource {}
|
||||
impl api::PreVerify for Cybersource {}
|
||||
|
||||
impl
|
||||
services::ConnectorIntegration<
|
||||
api::Verify,
|
||||
types::VerifyRequestData,
|
||||
types::PaymentsResponseData,
|
||||
> for Cybersource
|
||||
{
|
||||
}
|
||||
|
||||
impl api::PaymentSession for Cybersource {}
|
||||
|
||||
impl
|
||||
services::ConnectorIntegration<
|
||||
api::Session,
|
||||
types::PaymentsSessionData,
|
||||
types::PaymentsResponseData,
|
||||
> for Cybersource
|
||||
{
|
||||
}
|
||||
|
||||
impl
|
||||
services::ConnectorIntegration<
|
||||
api::Capture,
|
||||
types::PaymentsCaptureData,
|
||||
types::PaymentsResponseData,
|
||||
> for Cybersource
|
||||
{
|
||||
}
|
||||
|
||||
impl
|
||||
services::ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsResponseData>
|
||||
for Cybersource
|
||||
{
|
||||
}
|
||||
|
||||
impl
|
||||
services::ConnectorIntegration<
|
||||
api::Authorize,
|
||||
types::PaymentsAuthorizeData,
|
||||
types::PaymentsResponseData,
|
||||
> for Cybersource
|
||||
{
|
||||
fn get_headers(
|
||||
&self,
|
||||
_req: &types::PaymentsAuthorizeRouterData,
|
||||
) -> CustomResult<Vec<(String, String)>, errors::ConnectorError> {
|
||||
let headers = vec![
|
||||
(
|
||||
headers::CONTENT_TYPE.to_string(),
|
||||
types::PaymentsAuthorizeType::get_content_type(self).to_string(),
|
||||
),
|
||||
(
|
||||
headers::ACCEPT.to_string(),
|
||||
"application/hal+json;charset=utf-8".to_string(),
|
||||
),
|
||||
];
|
||||
Ok(headers)
|
||||
}
|
||||
|
||||
fn get_content_type(&self) -> &'static str {
|
||||
"application/json;charset=utf-8"
|
||||
}
|
||||
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &types::PaymentsAuthorizeRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Ok(format!(
|
||||
"{}pts/v2/payments/",
|
||||
api::ConnectorCommon::base_url(self, connectors)
|
||||
))
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
&self,
|
||||
req: &types::PaymentsAuthorizeRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
let date = OffsetDateTime::now_utc();
|
||||
|
||||
let cybersource_req =
|
||||
utils::Encode::<cybersource::CybersourcePaymentsRequest>::convert_and_encode(req)
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
let auth: cybersource::CybersourceAuthType =
|
||||
cybersource::CybersourceAuthType::try_from(&req.connector_auth_type)?;
|
||||
let merchant_account = auth.merchant_account.clone();
|
||||
|
||||
let cybersource_host = Url::parse(connectors.cybersource.base_url.as_str())
|
||||
.into_report()
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
match cybersource_host.host_str() {
|
||||
Some(host) => {
|
||||
let signature = self.generate_signature(
|
||||
auth,
|
||||
host.to_string(),
|
||||
"/pts/v2/payments/",
|
||||
&cybersource_req,
|
||||
date,
|
||||
)?;
|
||||
let headers = vec![
|
||||
(
|
||||
"Digest".to_string(),
|
||||
format!(
|
||||
"SHA-256={}",
|
||||
self.generate_digest(cybersource_req.as_bytes())
|
||||
),
|
||||
),
|
||||
("v-c-merchant-id".to_string(), merchant_account),
|
||||
("Date".to_string(), date.to_string()),
|
||||
("Host".to_string(), host.to_string()),
|
||||
("Signature".to_string(), signature),
|
||||
];
|
||||
let request = services::RequestBuilder::new()
|
||||
.method(services::Method::Post)
|
||||
.url(&types::PaymentsAuthorizeType::get_url(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.headers(headers)
|
||||
.headers(types::PaymentsAuthorizeType::get_headers(self, req)?)
|
||||
.body(Some(cybersource_req))
|
||||
.build();
|
||||
|
||||
Ok(Some(request))
|
||||
}
|
||||
None => Err(errors::ConnectorError::RequestEncodingFailed.into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_response(
|
||||
&self,
|
||||
data: &types::PaymentsAuthorizeRouterData,
|
||||
res: types::Response,
|
||||
) -> CustomResult<types::PaymentsAuthorizeRouterData, errors::ConnectorError> {
|
||||
let response: cybersource::CybersourcePaymentsResponse = res
|
||||
.response
|
||||
.parse_struct("Cybersource PaymentResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
logger::debug!(cybersourcepayments_create_response=?response);
|
||||
types::ResponseRouterData {
|
||||
response,
|
||||
data: data.clone(),
|
||||
http_code: res.status_code,
|
||||
}
|
||||
.try_into()
|
||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)
|
||||
}
|
||||
|
||||
fn get_error_response(
|
||||
&self,
|
||||
res: Bytes,
|
||||
) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
|
||||
let response: cybersource::ErrorResponse = res
|
||||
.parse_struct("Cybersource ErrorResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
Ok(types::ErrorResponse {
|
||||
code: consts::NO_ERROR_CODE.to_string(),
|
||||
message: response
|
||||
.message
|
||||
.unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()),
|
||||
reason: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl
|
||||
services::ConnectorIntegration<
|
||||
api::Void,
|
||||
types::PaymentsCancelData,
|
||||
types::PaymentsResponseData,
|
||||
> for Cybersource
|
||||
{
|
||||
}
|
||||
|
||||
impl api::Refund for Cybersource {}
|
||||
impl api::RefundExecute for Cybersource {}
|
||||
impl api::RefundSync for Cybersource {}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl services::ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsResponseData>
|
||||
for Cybersource
|
||||
{
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl services::ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponseData>
|
||||
for Cybersource
|
||||
{
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl api::IncomingWebhook for Cybersource {
|
||||
fn get_webhook_object_reference_id(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("cybersource".to_string()).into())
|
||||
}
|
||||
|
||||
fn get_webhook_event_type(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
) -> CustomResult<api::IncomingWebhookEvent, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("cybersource".to_string()).into())
|
||||
}
|
||||
|
||||
fn get_webhook_resource_object(
|
||||
&self,
|
||||
_body: &[u8],
|
||||
) -> CustomResult<serde_json::Value, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("cybersource".to_string()).into())
|
||||
}
|
||||
}
|
||||
|
||||
impl services::ConnectorRedirectResponse for Cybersource {}
|
||||
316
crates/router/src/connector/cybersource/transformers.rs
Normal file
316
crates/router/src/connector/cybersource/transformers.rs
Normal file
@ -0,0 +1,316 @@
|
||||
use api_models::payments;
|
||||
use common_utils::pii;
|
||||
use masking::Secret;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
core::errors,
|
||||
pii::PeekInterface,
|
||||
types::{self, api, storage::enums},
|
||||
};
|
||||
|
||||
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CybersourcePaymentsRequest {
|
||||
processing_information: ProcessingInformation,
|
||||
payment_information: PaymentInformation,
|
||||
order_information: OrderInformationWithBill,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||
pub struct ProcessingInformation {
|
||||
capture: bool,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||
pub struct PaymentInformation {
|
||||
card: Card,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Card {
|
||||
number: String,
|
||||
expiration_month: String,
|
||||
expiration_year: String,
|
||||
security_code: String,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct OrderInformationWithBill {
|
||||
amount_details: Amount,
|
||||
bill_to: BillTo,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct OrderInformation {
|
||||
amount_details: Amount,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Amount {
|
||||
total_amount: String,
|
||||
currency: String,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BillTo {
|
||||
first_name: Secret<String>,
|
||||
last_name: Secret<String>,
|
||||
address1: Secret<String>,
|
||||
locality: String,
|
||||
administrative_area: Secret<String>,
|
||||
postal_code: Secret<String>,
|
||||
country: String,
|
||||
email: Secret<String, pii::Email>,
|
||||
phone_number: Secret<String>,
|
||||
}
|
||||
|
||||
// for cybersource each item in Billing is mandatory
|
||||
fn build_bill_to(
|
||||
address_details: payments::Address,
|
||||
email: Secret<String, pii::Email>,
|
||||
phone_number: Secret<String>,
|
||||
) -> Option<BillTo> {
|
||||
if let Some(api_models::payments::AddressDetails {
|
||||
first_name: Some(f_name),
|
||||
last_name: Some(last_name),
|
||||
line1: Some(address1),
|
||||
city: Some(city),
|
||||
line2: Some(administrative_area),
|
||||
zip: Some(postal_code),
|
||||
country: Some(country),
|
||||
..
|
||||
}) = address_details.address
|
||||
{
|
||||
Some(BillTo {
|
||||
first_name: f_name,
|
||||
last_name,
|
||||
address1,
|
||||
locality: city,
|
||||
administrative_area,
|
||||
postal_code,
|
||||
country,
|
||||
email,
|
||||
phone_number,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&types::PaymentsAuthorizeRouterData> for CybersourcePaymentsRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> {
|
||||
match item.request.payment_method_data {
|
||||
api::PaymentMethod::Card(ref ccard) => {
|
||||
let address = item
|
||||
.address
|
||||
.billing
|
||||
.clone()
|
||||
.ok_or(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
let phone = address
|
||||
.clone()
|
||||
.phone
|
||||
.ok_or(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
let phone_number = phone
|
||||
.number
|
||||
.ok_or(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
let country_code = phone
|
||||
.country_code
|
||||
.ok_or(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
let number_with_code =
|
||||
Secret::new(format!("{}{}", country_code, phone_number.peek()));
|
||||
let email = item
|
||||
.request
|
||||
.email
|
||||
.clone()
|
||||
.ok_or(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
let bill_to = build_bill_to(address, email, number_with_code)
|
||||
.ok_or(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
|
||||
let order_information = OrderInformationWithBill {
|
||||
amount_details: Amount {
|
||||
total_amount: item.request.amount.to_string(),
|
||||
currency: item.request.currency.to_string().to_uppercase(),
|
||||
},
|
||||
bill_to,
|
||||
};
|
||||
|
||||
let payment_information = PaymentInformation {
|
||||
card: Card {
|
||||
number: ccard.card_number.peek().clone(),
|
||||
expiration_month: ccard.card_exp_month.peek().clone(),
|
||||
expiration_year: ccard.card_exp_year.peek().clone(),
|
||||
security_code: ccard.card_cvc.peek().clone(),
|
||||
},
|
||||
};
|
||||
|
||||
let processing_information = ProcessingInformation {
|
||||
capture: matches!(
|
||||
item.request.capture_method,
|
||||
Some(enums::CaptureMethod::Automatic) | None
|
||||
),
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
processing_information,
|
||||
payment_information,
|
||||
order_information,
|
||||
})
|
||||
}
|
||||
_ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CybersourceAuthType {
|
||||
pub(super) api_key: String,
|
||||
pub(super) merchant_account: String,
|
||||
pub(super) api_secret: String,
|
||||
}
|
||||
|
||||
impl TryFrom<&types::ConnectorAuthType> for CybersourceAuthType {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(auth_type: &types::ConnectorAuthType) -> Result<Self, Self::Error> {
|
||||
if let types::ConnectorAuthType::SignatureKey {
|
||||
api_key,
|
||||
key1,
|
||||
api_secret,
|
||||
} = auth_type
|
||||
{
|
||||
Ok(Self {
|
||||
api_key: api_key.to_string(),
|
||||
merchant_account: key1.to_string(),
|
||||
api_secret: api_secret.to_string(),
|
||||
})
|
||||
} else {
|
||||
Err(errors::ConnectorError::FailedToObtainAuthType)?
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Default, Clone, Deserialize, Eq, PartialEq)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
pub enum CybersourcePaymentStatus {
|
||||
Authorized,
|
||||
Succeeded,
|
||||
Failed,
|
||||
#[default]
|
||||
Processing,
|
||||
}
|
||||
|
||||
impl From<CybersourcePaymentStatus> for enums::AttemptStatus {
|
||||
fn from(item: CybersourcePaymentStatus) -> Self {
|
||||
match item {
|
||||
CybersourcePaymentStatus::Authorized => Self::Authorized,
|
||||
CybersourcePaymentStatus::Succeeded => Self::Charged,
|
||||
CybersourcePaymentStatus::Failed => Self::Failure,
|
||||
CybersourcePaymentStatus::Processing => Self::Authorizing,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Deserialize, Eq, PartialEq)]
|
||||
pub struct CybersourcePaymentsResponse {
|
||||
id: String,
|
||||
status: CybersourcePaymentStatus,
|
||||
}
|
||||
|
||||
impl TryFrom<types::PaymentsResponseRouterData<CybersourcePaymentsResponse>>
|
||||
for types::PaymentsAuthorizeRouterData
|
||||
{
|
||||
type Error = error_stack::Report<errors::ParsingError>;
|
||||
fn try_from(
|
||||
item: types::PaymentsResponseRouterData<CybersourcePaymentsResponse>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
status: item.response.status.into(),
|
||||
response: Ok(types::PaymentsResponseData::TransactionResponse {
|
||||
resource_id: types::ResponseId::ConnectorTransactionId(item.response.id),
|
||||
redirection_data: None,
|
||||
redirect: false,
|
||||
mandate_reference: None,
|
||||
}),
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ErrorResponse {
|
||||
pub error_information: Option<ErrorInformation>,
|
||||
pub status: String,
|
||||
pub message: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
pub struct ErrorInformation {
|
||||
pub message: String,
|
||||
pub reason: String,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize)]
|
||||
pub struct CybersourceRefundRequest {
|
||||
order_information: OrderInformation,
|
||||
}
|
||||
|
||||
impl<F> TryFrom<&types::RefundsRouterData<F>> for CybersourceRefundRequest {
|
||||
type Error = error_stack::Report<errors::ParsingError>;
|
||||
fn try_from(item: &types::RefundsRouterData<F>) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
order_information: OrderInformation {
|
||||
amount_details: Amount {
|
||||
total_amount: item.request.amount.to_string(),
|
||||
currency: item.request.currency.to_string(),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Default, Deserialize, Clone)]
|
||||
pub enum RefundStatus {
|
||||
Succeeded,
|
||||
Failed,
|
||||
#[default]
|
||||
Processing,
|
||||
}
|
||||
|
||||
impl From<RefundStatus> for enums::RefundStatus {
|
||||
fn from(item: RefundStatus) -> Self {
|
||||
match item {
|
||||
self::RefundStatus::Succeeded => Self::Success,
|
||||
self::RefundStatus::Failed => Self::Failure,
|
||||
self::RefundStatus::Processing => Self::Pending,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Deserialize)]
|
||||
pub struct CybersourceRefundResponse {
|
||||
pub id: String,
|
||||
pub status: RefundStatus,
|
||||
}
|
||||
|
||||
impl TryFrom<types::RefundsResponseRouterData<api::RSync, CybersourceRefundResponse>>
|
||||
for types::RefundsRouterData<api::RSync>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ParsingError>;
|
||||
fn try_from(
|
||||
item: types::RefundsResponseRouterData<api::RSync, CybersourceRefundResponse>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
response: Ok(types::RefundsResponseData {
|
||||
connector_refund_id: item.response.id,
|
||||
refund_status: enums::RefundStatus::from(item.response.status),
|
||||
}),
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -469,6 +469,7 @@ where
|
||||
pub refunds: Vec<storage::Refund>,
|
||||
pub sessions_token: Vec<api::SessionToken>,
|
||||
pub card_cvc: Option<pii::Secret<String>>,
|
||||
pub email: Option<masking::Secret<String, pii::Email>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
||||
@ -113,6 +113,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsCancelRequest>
|
||||
payment_attempt,
|
||||
currency,
|
||||
amount,
|
||||
email: None,
|
||||
mandate_id: None,
|
||||
setup_mandate: None,
|
||||
token: None,
|
||||
|
||||
@ -125,6 +125,7 @@ impl<F: Send + Clone> GetTracker<F, payments::PaymentData<F>, api::PaymentsCaptu
|
||||
currency,
|
||||
force_sync: None,
|
||||
amount,
|
||||
email: None,
|
||||
mandate_id: None,
|
||||
setup_mandate: None,
|
||||
token: None,
|
||||
|
||||
@ -142,6 +142,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
|
||||
currency,
|
||||
connector_response,
|
||||
amount,
|
||||
email: request.email.clone(),
|
||||
mandate_id: None,
|
||||
setup_mandate,
|
||||
token,
|
||||
|
||||
@ -214,6 +214,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
|
||||
payment_attempt,
|
||||
currency,
|
||||
amount,
|
||||
email: request.email.clone(),
|
||||
mandate_id,
|
||||
setup_mandate,
|
||||
token,
|
||||
|
||||
@ -136,6 +136,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::VerifyRequest> for Paym
|
||||
/// currency and amount are irrelevant in this scenario
|
||||
currency: storage_enums::Currency::default(),
|
||||
amount: api::Amount::Zero,
|
||||
email: None,
|
||||
mandate_id: None,
|
||||
setup_mandate: request.mandate_data.clone(),
|
||||
token: request.payment_token.clone(),
|
||||
|
||||
@ -13,6 +13,7 @@ use crate::{
|
||||
payments::{self, helpers, operations, PaymentData},
|
||||
},
|
||||
db::StorageInterface,
|
||||
pii,
|
||||
pii::Secret,
|
||||
routes::AppState,
|
||||
types::{
|
||||
@ -131,6 +132,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsSessionRequest>
|
||||
payment_attempt,
|
||||
currency,
|
||||
amount,
|
||||
email: None::<Secret<String, pii::Email>>,
|
||||
mandate_id: None,
|
||||
token: None,
|
||||
setup_mandate: None,
|
||||
|
||||
@ -12,6 +12,7 @@ use crate::{
|
||||
payments::{helpers, operations, CustomerDetails, PaymentAddress, PaymentData},
|
||||
},
|
||||
db::StorageInterface,
|
||||
pii,
|
||||
pii::Secret,
|
||||
routes::AppState,
|
||||
types::{
|
||||
@ -128,6 +129,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsStartRequest> f
|
||||
payment_intent,
|
||||
currency,
|
||||
amount,
|
||||
email: None::<Secret<String, pii::Email>>,
|
||||
mandate_id: None,
|
||||
connector_response,
|
||||
setup_mandate: None,
|
||||
|
||||
@ -275,6 +275,7 @@ async fn get_tracker_for_sync<
|
||||
connector_response,
|
||||
currency,
|
||||
amount,
|
||||
email: None,
|
||||
mandate_id: None,
|
||||
setup_mandate: None,
|
||||
token: None,
|
||||
|
||||
@ -164,6 +164,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
|
||||
payment_attempt,
|
||||
currency,
|
||||
amount,
|
||||
email: request.email.clone(),
|
||||
mandate_id,
|
||||
token,
|
||||
setup_mandate,
|
||||
|
||||
@ -433,6 +433,7 @@ impl<F: Clone> TryFrom<PaymentData<F>> for types::PaymentsAuthorizeData {
|
||||
amount: payment_data.amount.into(),
|
||||
currency: payment_data.currency,
|
||||
browser_info,
|
||||
email: payment_data.email,
|
||||
order_details,
|
||||
})
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ pub mod transformers;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub use api_models::enums::Connector;
|
||||
use common_utils::pii::Email;
|
||||
use error_stack::{IntoReport, ResultExt};
|
||||
|
||||
use self::{api::payments, storage::enums as storage_enums};
|
||||
@ -90,6 +91,7 @@ pub struct RouterData<Flow, Request, Response> {
|
||||
pub struct PaymentsAuthorizeData {
|
||||
pub payment_method_data: payments::PaymentMethod,
|
||||
pub amount: i64,
|
||||
pub email: Option<masking::Secret<String, Email>>,
|
||||
pub currency: storage_enums::Currency,
|
||||
pub confirm: bool,
|
||||
pub statement_descriptor_suffix: Option<String>,
|
||||
|
||||
@ -146,6 +146,7 @@ impl ConnectorData {
|
||||
"braintree" => Ok(Box::new(&connector::Braintree)),
|
||||
"klarna" => Ok(Box::new(&connector::Klarna)),
|
||||
"applepay" => Ok(Box::new(&connector::Applepay)),
|
||||
"cybersource" => Ok(Box::new(&connector::Cybersource)),
|
||||
"shift4" => Ok(Box::new(&connector::Shift4)),
|
||||
_ => Err(report!(errors::UnexpectedError)
|
||||
.attach_printable(format!("invalid connector name: {connector_name}")))
|
||||
|
||||
@ -48,6 +48,7 @@ fn construct_payment_router_data() -> types::PaymentsAuthorizeRouterData {
|
||||
capture_method: None,
|
||||
browser_info: None,
|
||||
order_details: None,
|
||||
email: None,
|
||||
},
|
||||
response: Err(types::ErrorResponse::default()),
|
||||
payment_method_id: None,
|
||||
|
||||
@ -48,6 +48,7 @@ fn construct_payment_router_data() -> types::PaymentsAuthorizeRouterData {
|
||||
capture_method: None,
|
||||
browser_info: None,
|
||||
order_details: None,
|
||||
email: None,
|
||||
},
|
||||
payment_method_id: None,
|
||||
response: Err(types::ErrorResponse::default()),
|
||||
|
||||
@ -45,6 +45,7 @@ fn construct_payment_router_data() -> types::PaymentsAuthorizeRouterData {
|
||||
capture_method: None,
|
||||
browser_info: None,
|
||||
order_details: None,
|
||||
email: None,
|
||||
},
|
||||
response: Err(types::ErrorResponse::default()),
|
||||
payment_method_id: None,
|
||||
|
||||
@ -134,6 +134,7 @@ impl Default for PaymentAuthorizeType {
|
||||
setup_mandate_details: None,
|
||||
browser_info: None,
|
||||
order_details: None,
|
||||
email: None,
|
||||
};
|
||||
Self(data)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user