feat(connector): [Globepay] add authorize and psync flow (#1639)

Signed-off-by: chikke srujan <121822803+srujanchikke@users.noreply.github.com>
Co-authored-by: Arjun Karthik <m.arjunkarthik@gmail.com>
This commit is contained in:
chikke srujan
2023-07-13 14:06:25 +05:30
committed by GitHub
parent f7d369afa8
commit c119bfdd7e
10 changed files with 334 additions and 284 deletions

View File

@ -642,7 +642,7 @@ pub enum Connector {
Fiserv, Fiserv,
Forte, Forte,
Globalpay, Globalpay,
// Globepay, added as template code for future usage Globepay,
Klarna, Klarna,
Mollie, Mollie,
Multisafepay, Multisafepay,
@ -747,7 +747,7 @@ pub enum RoutableConnectors {
Fiserv, Fiserv,
Forte, Forte,
Globalpay, Globalpay,
// Globepay, added as template code for future usage Globepay,
Iatapay, Iatapay,
Klarna, Klarna,
Mollie, Mollie,

View File

@ -895,6 +895,8 @@ pub struct BankDebitBilling {
#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)] #[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub enum WalletData { pub enum WalletData {
/// The wallet data for Ali Pay QrCode
AliPay(Box<AliPay>),
/// The wallet data for Ali Pay redirect /// The wallet data for Ali Pay redirect
AliPayRedirect(AliPayRedirection), AliPayRedirect(AliPayRedirection),
/// The wallet data for Ali Pay HK redirect /// The wallet data for Ali Pay HK redirect
@ -969,6 +971,9 @@ pub struct WeChatPay {}
#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)] #[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)]
pub struct PaypalRedirection {} pub struct PaypalRedirection {}
#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)]
pub struct AliPay {}
#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)] #[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)]
pub struct AliPayRedirection {} pub struct AliPayRedirection {}

View File

@ -765,6 +765,32 @@ impl Default for super::settings::RequiredFields {
)]), )]),
}, },
), ),
(
enums::PaymentMethodType::AliPay,
ConnectorFields {
fields: HashMap::from([(
enums::Connector::Globepay,
vec![RequiredFieldInfo {
required_field: "description".to_string(),
display_name: "description".to_string(),
field_type: enums::FieldType::Text,
}],
)]),
},
),
(
enums::PaymentMethodType::WeChatPay,
ConnectorFields {
fields: HashMap::from([(
enums::Connector::Globepay,
vec![RequiredFieldInfo {
required_field: "description".to_string(),
display_name: "description".to_string(),
field_type: enums::FieldType::Text,
}],
)]),
},
),
])), ])),
), ),
( (

View File

@ -2,22 +2,24 @@ mod transformers;
use std::fmt::Debug; use std::fmt::Debug;
use common_utils::crypto::{self, GenerateDigest};
use error_stack::{IntoReport, ResultExt}; use error_stack::{IntoReport, ResultExt};
use hex::encode;
use masking::ExposeInterface; use masking::ExposeInterface;
use rand::distributions::DistString;
use time::OffsetDateTime;
use transformers as globepay; use transformers as globepay;
use crate::{ use crate::{
configs::settings, configs::settings,
consts,
core::errors::{self, CustomResult}, core::errors::{self, CustomResult},
headers, headers,
services::{ services::{self, request, ConnectorIntegration},
self,
request::{self, Mask},
ConnectorIntegration,
},
types::{ types::{
self, self,
api::{self, ConnectorCommon, ConnectorCommonExt}, api::{self, ConnectorCommon, ConnectorCommonExt},
storage::enums,
ErrorResponse, Response, ErrorResponse, Response,
}, },
utils::{self, BytesExt}, utils::{self, BytesExt},
@ -55,21 +57,44 @@ where
{ {
fn build_headers( fn build_headers(
&self, &self,
req: &types::RouterData<Flow, Request, Response>, _req: &types::RouterData<Flow, Request, Response>,
_connectors: &settings::Connectors, _connectors: &settings::Connectors,
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> { ) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
let mut header = vec![( let header = vec![(
headers::CONTENT_TYPE.to_string(), headers::CONTENT_TYPE.to_string(),
types::PaymentsAuthorizeType::get_content_type(self) self.get_content_type().to_string().into(),
.to_string()
.into(),
)]; )];
let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
header.append(&mut api_key);
Ok(header) Ok(header)
} }
} }
fn get_globlepay_query_params(
connector_auth_type: &types::ConnectorAuthType,
) -> CustomResult<String, errors::ConnectorError> {
let auth_type = globepay::GlobepayAuthType::try_from(connector_auth_type)?;
let time = (OffsetDateTime::now_utc().unix_timestamp_nanos() / 1_000_000).to_string();
let nonce_str = rand::distributions::Alphanumeric.sample_string(&mut rand::thread_rng(), 12);
let valid_string = format!(
"{}&{time}&{nonce_str}&{}",
auth_type.partner_code.expose(),
auth_type.credential_code.expose()
);
let digest = crypto::Sha256
.generate_digest(valid_string.as_bytes())
.change_context(errors::ConnectorError::RequestEncodingFailed)
.attach_printable("error encoding the query params")?;
let sign = encode(digest).to_lowercase();
let param = format!("?sign={sign}&time={time}&nonce_str={nonce_str}");
Ok(param)
}
fn get_partner_code(
connector_auth_type: &types::ConnectorAuthType,
) -> CustomResult<String, errors::ConnectorError> {
let auth_type = globepay::GlobepayAuthType::try_from(connector_auth_type)?;
Ok(auth_type.partner_code.expose())
}
impl ConnectorCommon for Globepay { impl ConnectorCommon for Globepay {
fn id(&self) -> &'static str { fn id(&self) -> &'static str {
"globepay" "globepay"
@ -83,18 +108,6 @@ impl ConnectorCommon for Globepay {
connectors.globepay.base_url.as_ref() connectors.globepay.base_url.as_ref()
} }
fn get_auth_header(
&self,
auth_type: &types::ConnectorAuthType,
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
let auth = globepay::GlobepayAuthType::try_from(auth_type)
.change_context(errors::ConnectorError::FailedToObtainAuthType)?;
Ok(vec![(
headers::AUTHORIZATION.to_string(),
auth.api_key.expose().into_masked(),
)])
}
fn build_error_response( fn build_error_response(
&self, &self,
res: Response, res: Response,
@ -106,9 +119,9 @@ impl ConnectorCommon for Globepay {
Ok(ErrorResponse { Ok(ErrorResponse {
status_code: res.status_code, status_code: res.status_code,
code: response.code, code: response.return_code.to_string(),
message: response.message, message: consts::NO_ERROR_MESSAGE.to_string(),
reason: response.reason, reason: Some(response.return_msg),
}) })
} }
} }
@ -116,7 +129,6 @@ impl ConnectorCommon for Globepay {
impl ConnectorIntegration<api::Session, types::PaymentsSessionData, types::PaymentsResponseData> impl ConnectorIntegration<api::Session, types::PaymentsSessionData, types::PaymentsResponseData>
for Globepay for Globepay
{ {
//TODO: implement sessions flow
} }
impl ConnectorIntegration<api::AccessTokenAuth, types::AccessTokenRequestData, types::AccessToken> impl ConnectorIntegration<api::AccessTokenAuth, types::AccessTokenRequestData, types::AccessToken>
@ -146,10 +158,24 @@ 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()) let query_params = get_globlepay_query_params(&req.connector_auth_type)?;
if req.request.capture_method == Some(enums::CaptureMethod::Automatic) {
Ok(format!(
"{}api/v1.0/gateway/partners/{}/orders/{}{query_params}",
self.base_url(connectors),
get_partner_code(&req.connector_auth_type)?,
req.payment_id
))
} else {
Err(errors::ConnectorError::FlowNotSupported {
flow: "Manual Capture".to_owned(),
connector: "Globepay".to_owned(),
}
.into())
}
} }
fn get_request_body( fn get_request_body(
@ -172,7 +198,7 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
) -> CustomResult<Option<services::Request>, errors::ConnectorError> { ) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
Ok(Some( Ok(Some(
services::RequestBuilder::new() services::RequestBuilder::new()
.method(services::Method::Post) .method(services::Method::Put)
.url(&types::PaymentsAuthorizeType::get_url( .url(&types::PaymentsAuthorizeType::get_url(
self, req, connectors, self, req, connectors,
)?) )?)
@ -226,10 +252,16 @@ 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()) let query_params = get_globlepay_query_params(&req.connector_auth_type)?;
Ok(format!(
"{}api/v1.0/gateway/partners/{}/orders/{}{query_params}",
self.base_url(connectors),
get_partner_code(&req.connector_auth_type)?,
req.payment_id
))
} }
fn build_request( fn build_request(
@ -252,7 +284,7 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe
data: &types::PaymentsSyncRouterData, data: &types::PaymentsSyncRouterData,
res: Response, res: Response,
) -> CustomResult<types::PaymentsSyncRouterData, errors::ConnectorError> { ) -> CustomResult<types::PaymentsSyncRouterData, errors::ConnectorError> {
let response: globepay::GlobepayPaymentsResponse = res let response: globepay::GlobepaySyncResponse = res
.response .response
.parse_struct("globepay PaymentsSyncResponse") .parse_struct("globepay PaymentsSyncResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?; .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
@ -274,72 +306,16 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe
impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::PaymentsResponseData> impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::PaymentsResponseData>
for Globepay for Globepay
{ {
fn get_headers( fn build_request(
&self,
req: &types::PaymentsCaptureRouterData,
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, &self,
_req: &types::PaymentsCaptureRouterData, _req: &types::PaymentsCaptureRouterData,
_connectors: &settings::Connectors, _connectors: &settings::Connectors,
) -> CustomResult<String, errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
}
fn get_request_body(
&self,
_req: &types::PaymentsCaptureRouterData,
) -> CustomResult<Option<types::RequestBody>, errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into())
}
fn build_request(
&self,
req: &types::PaymentsCaptureRouterData,
connectors: &settings::Connectors,
) -> CustomResult<Option<services::Request>, errors::ConnectorError> { ) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
Ok(Some( Err(errors::ConnectorError::FlowNotSupported {
services::RequestBuilder::new() flow: "Manual Capture".to_owned(),
.method(services::Method::Post) connector: "Globepay".to_owned(),
.url(&types::PaymentsCaptureType::get_url(self, req, connectors)?)
.attach_default_headers()
.headers(types::PaymentsCaptureType::get_headers(
self, req, connectors,
)?)
.body(types::PaymentsCaptureType::get_request_body(self, req)?)
.build(),
))
} }
.into())
fn handle_response(
&self,
data: &types::PaymentsCaptureRouterData,
res: Response,
) -> CustomResult<types::PaymentsCaptureRouterData, errors::ConnectorError> {
let response: globepay::GlobepayPaymentsResponse = res
.response
.parse_struct("Globepay PaymentsCaptureResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
types::RouterData::try_from(types::ResponseRouterData {
response,
data: data.clone(),
http_code: res.status_code,
})
}
fn get_error_response(
&self,
res: Response,
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
self.build_error_response(res)
} }
} }
@ -351,138 +327,22 @@ impl ConnectorIntegration<api::Void, types::PaymentsCancelData, types::PaymentsR
impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsResponseData> impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsResponseData>
for Globepay for Globepay
{ {
fn get_headers( fn build_request(
&self,
req: &types::RefundsRouterData<api::Execute>,
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, &self,
_req: &types::RefundsRouterData<api::Execute>, _req: &types::RefundsRouterData<api::Execute>,
_connectors: &settings::Connectors, _connectors: &settings::Connectors,
) -> CustomResult<String, errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
}
fn get_request_body(
&self,
req: &types::RefundsRouterData<api::Execute>,
) -> CustomResult<Option<types::RequestBody>, errors::ConnectorError> {
let req_obj = globepay::GlobepayRefundRequest::try_from(req)?;
let globepay_req = types::RequestBody::log_and_get_request_body(
&req_obj,
utils::Encode::<globepay::GlobepayRefundRequest>::encode_to_string_of_json,
)
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
Ok(Some(globepay_req))
}
fn build_request(
&self,
req: &types::RefundsRouterData<api::Execute>,
connectors: &settings::Connectors,
) -> CustomResult<Option<services::Request>, errors::ConnectorError> { ) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
let request = services::RequestBuilder::new() Err(errors::ConnectorError::NotImplemented("Refund".to_string()).into())
.method(services::Method::Post)
.url(&types::RefundExecuteType::get_url(self, req, connectors)?)
.attach_default_headers()
.headers(types::RefundExecuteType::get_headers(
self, req, connectors,
)?)
.body(types::RefundExecuteType::get_request_body(self, req)?)
.build();
Ok(Some(request))
}
fn handle_response(
&self,
data: &types::RefundsRouterData<api::Execute>,
res: Response,
) -> CustomResult<types::RefundsRouterData<api::Execute>, errors::ConnectorError> {
let response: globepay::RefundResponse = res
.response
.parse_struct("globepay RefundResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
types::RouterData::try_from(types::ResponseRouterData {
response,
data: data.clone(),
http_code: res.status_code,
})
}
fn get_error_response(
&self,
res: Response,
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
self.build_error_response(res)
} }
} }
impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponseData> for Globepay { impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponseData> for Globepay {
fn get_headers( fn build_request(
&self,
req: &types::RefundSyncRouterData,
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, &self,
_req: &types::RefundSyncRouterData, _req: &types::RefundSyncRouterData,
_connectors: &settings::Connectors, _connectors: &settings::Connectors,
) -> CustomResult<String, errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
}
fn build_request(
&self,
req: &types::RefundSyncRouterData,
connectors: &settings::Connectors,
) -> CustomResult<Option<services::Request>, errors::ConnectorError> { ) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
Ok(Some( Err(errors::ConnectorError::NotImplemented("Refund Sync".to_string()).into())
services::RequestBuilder::new()
.method(services::Method::Get)
.url(&types::RefundSyncType::get_url(self, req, connectors)?)
.attach_default_headers()
.headers(types::RefundSyncType::get_headers(self, req, connectors)?)
.body(types::RefundSyncType::get_request_body(self, req)?)
.build(),
))
}
fn handle_response(
&self,
data: &types::RefundSyncRouterData,
res: Response,
) -> CustomResult<types::RefundSyncRouterData, errors::ConnectorError> {
let response: globepay::RefundResponse = res
.response
.parse_struct("globepay RefundSyncResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
types::RouterData::try_from(types::ResponseRouterData {
response,
data: data.clone(),
http_code: res.status_code,
})
}
fn get_error_response(
&self,
res: Response,
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
self.build_error_response(res)
} }
} }

View File

@ -1,90 +1,115 @@
use error_stack::ResultExt;
use masking::Secret; use masking::Secret;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
connector::utils::PaymentsAuthorizeRequestData, connector::utils::RouterData,
consts,
core::errors, core::errors,
types::{self, api, storage::enums}, types::{self, api, storage::enums},
}; };
#[derive(Default, Debug, Serialize, Eq, PartialEq)] #[derive(Debug, Serialize)]
pub struct GlobepayPaymentsRequest { pub struct GlobepayPaymentsRequest {
amount: i64, price: i64,
card: GlobepayCard, description: String,
currency: enums::Currency,
channel: GlobepayChannel,
} }
#[derive(Default, Debug, Serialize, Eq, PartialEq)] #[derive(Debug, Serialize)]
pub struct GlobepayCard { pub enum GlobepayChannel {
name: Secret<String>, Alipay,
number: cards::CardNumber, Wechat,
expiry_month: Secret<String>,
expiry_year: Secret<String>,
cvc: Secret<String>,
complete: bool,
} }
impl TryFrom<&types::PaymentsAuthorizeRouterData> for GlobepayPaymentsRequest { impl TryFrom<&types::PaymentsAuthorizeRouterData> for GlobepayPaymentsRequest {
type Error = error_stack::Report<errors::ConnectorError>; type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> { fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> {
match item.request.payment_method_data.clone() { let channel: GlobepayChannel = match &item.request.payment_method_data {
api::PaymentMethodData::Card(req_card) => { api::PaymentMethodData::Wallet(ref wallet_data) => match wallet_data {
let card = GlobepayCard { api::WalletData::AliPay(_) => GlobepayChannel::Alipay,
name: req_card.card_holder_name, api::WalletData::WeChatPay(_) => GlobepayChannel::Wechat,
number: req_card.card_number, _ => Err(errors::ConnectorError::NotImplemented(
expiry_month: req_card.card_exp_month, "Payment method".to_string(),
expiry_year: req_card.card_exp_year, ))?,
cvc: req_card.card_cvc, },
complete: item.request.is_auto_capture()?, _ => Err(errors::ConnectorError::NotImplemented(
"Payment method".to_string(),
))?,
}; };
let description = item.get_description()?;
Ok(Self { Ok(Self {
amount: item.request.amount, price: item.request.amount,
card, description,
currency: item.request.currency,
channel,
}) })
} }
_ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()),
}
}
} }
pub struct GlobepayAuthType { pub struct GlobepayAuthType {
pub(super) api_key: Secret<String>, pub(super) partner_code: Secret<String>,
pub(super) credential_code: Secret<String>,
} }
impl TryFrom<&types::ConnectorAuthType> for GlobepayAuthType { impl TryFrom<&types::ConnectorAuthType> for GlobepayAuthType {
type Error = error_stack::Report<errors::ConnectorError>; type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(auth_type: &types::ConnectorAuthType) -> Result<Self, Self::Error> { fn try_from(auth_type: &types::ConnectorAuthType) -> Result<Self, Self::Error> {
match auth_type { match auth_type {
types::ConnectorAuthType::HeaderKey { api_key } => Ok(Self { types::ConnectorAuthType::BodyKey { api_key, key1 } => Ok(Self {
api_key: Secret::new(api_key.to_string()), partner_code: Secret::new(api_key.to_owned()),
credential_code: Secret::new(key1.to_owned()),
}), }),
_ => Err(errors::ConnectorError::FailedToObtainAuthType.into()), _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
} }
} }
} }
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum GlobepayPaymentStatus { pub enum GlobepayPaymentStatus {
Succeeded, Success,
Failed, Exists,
#[default]
Processing,
} }
impl From<GlobepayPaymentStatus> for enums::AttemptStatus { impl From<GlobepayPaymentStatus> for enums::AttemptStatus {
fn from(item: GlobepayPaymentStatus) -> Self { fn from(item: GlobepayPaymentStatus) -> Self {
match item { match item {
GlobepayPaymentStatus::Succeeded => Self::Charged, GlobepayPaymentStatus::Success => Self::AuthenticationPending, // this connector only have redirection flows so "Success" is mapped to authenticatoin pending ,ref = "https://pay.globepay.co/docs/en/#api-QRCode-NewQRCode"
GlobepayPaymentStatus::Failed => Self::Failure, GlobepayPaymentStatus::Exists => Self::Failure,
GlobepayPaymentStatus::Processing => Self::Authorizing,
} }
} }
} }
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)] #[derive(Debug, Deserialize, Serialize)]
pub struct GlobepayConnectorMetadata {
image_data_url: url::Url,
}
#[derive(Debug, Deserialize)]
pub struct GlobepayPaymentsResponse { pub struct GlobepayPaymentsResponse {
status: GlobepayPaymentStatus, result_code: Option<GlobepayPaymentStatus>,
id: String, order_id: Option<String>,
qrcode_img: Option<url::Url>,
return_code: GlobepayReturnCode, //Execution result
return_msg: Option<String>,
}
#[derive(Debug, Deserialize, PartialEq, strum::Display)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum GlobepayReturnCode {
Success,
OrderNotExist,
OrderMismatch,
Systemerror,
InvalidShortId,
SignTimeout,
InvalidSign,
ParamInvalid,
NotPermitted,
InvalidChannel,
DuplicateOrderId,
} }
impl<F, T> impl<F, T>
@ -100,10 +125,104 @@ impl<F, T>
types::PaymentsResponseData, types::PaymentsResponseData,
>, >,
) -> Result<Self, Self::Error> { ) -> Result<Self, Self::Error> {
if item.response.return_code == GlobepayReturnCode::Success {
let globepay_metadata = GlobepayConnectorMetadata {
image_data_url: item
.response
.qrcode_img
.ok_or(errors::ConnectorError::ResponseHandlingFailed)?,
};
let connector_metadata = Some(common_utils::ext_traits::Encode::<
GlobepayConnectorMetadata,
>::encode_to_value(&globepay_metadata))
.transpose()
.change_context(errors::ConnectorError::ResponseHandlingFailed)?;
let globepay_status = item
.response
.result_code
.ok_or(errors::ConnectorError::ResponseHandlingFailed)?;
Ok(Self { Ok(Self {
status: enums::AttemptStatus::from(item.response.status), status: enums::AttemptStatus::from(globepay_status),
response: Ok(types::PaymentsResponseData::TransactionResponse { response: Ok(types::PaymentsResponseData::TransactionResponse {
resource_id: types::ResponseId::ConnectorTransactionId(item.response.id), resource_id: types::ResponseId::ConnectorTransactionId(
item.response
.order_id
.ok_or(errors::ConnectorError::ResponseHandlingFailed)?,
),
redirection_data: None,
mandate_reference: None,
connector_metadata,
network_txn_id: None,
connector_response_reference_id: None,
}),
..item.data
})
} else {
Ok(Self {
status: enums::AttemptStatus::Failure, //As this connector gives 200 in failed scenarios . if return_code is not success status is mapped to failure. ref = "https://pay.globepay.co/docs/en/#api-QRCode-NewQRCode"
response: Err(get_error_response(
item.response.return_code,
item.response.return_msg,
item.http_code,
)),
..item.data
})
}
}
}
#[derive(Debug, Deserialize)]
pub struct GlobepaySyncResponse {
pub result_code: Option<GlobepayPaymentPsyncStatus>,
pub order_id: Option<String>,
pub return_code: GlobepayReturnCode,
pub return_msg: Option<String>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum GlobepayPaymentPsyncStatus {
Paying,
CreateFail,
Closed,
PayFail,
PaySuccess,
}
impl From<GlobepayPaymentPsyncStatus> for enums::AttemptStatus {
fn from(item: GlobepayPaymentPsyncStatus) -> Self {
match item {
GlobepayPaymentPsyncStatus::PaySuccess => Self::Charged,
GlobepayPaymentPsyncStatus::PayFail
| GlobepayPaymentPsyncStatus::CreateFail
| GlobepayPaymentPsyncStatus::Closed => Self::Failure,
GlobepayPaymentPsyncStatus::Paying => Self::AuthenticationPending,
}
}
}
impl<F, T>
TryFrom<types::ResponseRouterData<F, GlobepaySyncResponse, T, types::PaymentsResponseData>>
for types::RouterData<F, T, types::PaymentsResponseData>
{
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
item: types::ResponseRouterData<F, GlobepaySyncResponse, T, types::PaymentsResponseData>,
) -> Result<Self, Self::Error> {
if item.response.return_code == GlobepayReturnCode::Success {
let globepay_status = item
.response
.result_code
.ok_or(errors::ConnectorError::ResponseHandlingFailed)?;
let globepay_id = item
.response
.order_id
.ok_or(errors::ConnectorError::ResponseHandlingFailed)?;
Ok(Self {
status: enums::AttemptStatus::from(globepay_status),
response: Ok(types::PaymentsResponseData::TransactionResponse {
resource_id: types::ResponseId::ConnectorTransactionId(globepay_id),
redirection_data: None, redirection_data: None,
mandate_reference: None, mandate_reference: None,
connector_metadata: None, connector_metadata: None,
@ -112,10 +231,34 @@ impl<F, T>
}), }),
..item.data ..item.data
}) })
} else {
Ok(Self {
status: enums::AttemptStatus::Failure, //As this connector gives 200 in failed scenarios . if return_code is not success status is mapped to failure. ref = "https://pay.globepay.co/docs/en/#api-QRCode-NewQRCode"
response: Err(get_error_response(
item.response.return_code,
item.response.return_msg,
item.http_code,
)),
..item.data
})
}
} }
} }
#[derive(Default, Debug, Serialize)] fn get_error_response(
return_code: GlobepayReturnCode,
return_msg: Option<String>,
status_code: u16,
) -> types::ErrorResponse {
types::ErrorResponse {
code: return_code.to_string(),
message: consts::NO_ERROR_MESSAGE.to_string(),
reason: return_msg,
status_code,
}
}
#[derive(Debug, Serialize)]
pub struct GlobepayRefundRequest { pub struct GlobepayRefundRequest {
pub amount: i64, pub amount: i64,
} }
@ -188,10 +331,9 @@ impl TryFrom<types::RefundsResponseRouterData<api::RSync, RefundResponse>>
} }
} }
#[derive(Default, Debug, Serialize, Deserialize, PartialEq)] #[derive(Debug, Deserialize)]
pub struct GlobepayErrorResponse { pub struct GlobepayErrorResponse {
pub status_code: u16, pub return_msg: String,
pub code: String, pub return_code: GlobepayReturnCode,
pub message: String, pub message: String,
pub reason: Option<String>,
} }

View File

@ -165,6 +165,7 @@ Never share your secret api keys. Keep them guarded and secure.
api_models::disputes::DisputeResponsePaymentsRetrieve, api_models::disputes::DisputeResponsePaymentsRetrieve,
api_models::payments::AddressDetails, api_models::payments::AddressDetails,
api_models::payments::BankDebitData, api_models::payments::BankDebitData,
api_models::payments::AliPay,
api_models::payments::AliPayRedirection, api_models::payments::AliPayRedirection,
api_models::payments::AliPayHkRedirection, api_models::payments::AliPayHkRedirection,
api_models::payments::MbWayRedirection, api_models::payments::MbWayRedirection,

View File

@ -232,6 +232,7 @@ impl ConnectorData {
enums::Connector::Fiserv => Ok(Box::new(&connector::Fiserv)), enums::Connector::Fiserv => Ok(Box::new(&connector::Fiserv)),
enums::Connector::Forte => Ok(Box::new(&connector::Forte)), enums::Connector::Forte => Ok(Box::new(&connector::Forte)),
enums::Connector::Globalpay => Ok(Box::new(&connector::Globalpay)), enums::Connector::Globalpay => Ok(Box::new(&connector::Globalpay)),
enums::Connector::Globepay => Ok(Box::new(&connector::Globepay)),
enums::Connector::Iatapay => Ok(Box::new(&connector::Iatapay)), enums::Connector::Iatapay => Ok(Box::new(&connector::Iatapay)),
enums::Connector::Klarna => Ok(Box::new(&connector::Klarna)), enums::Connector::Klarna => Ok(Box::new(&connector::Klarna)),
enums::Connector::Mollie => Ok(Box::new(&connector::Mollie)), enums::Connector::Mollie => Ok(Box::new(&connector::Mollie)),

View File

@ -144,8 +144,8 @@ api_key="Classic PMT API Key"
key1 = "Evoucher PMT API Key" key1 = "Evoucher PMT API Key"
[globepay] [globepay]
api_key="API Key" api_key = "Partner code"
key1 = "Credential code"
[powertranz] [powertranz]
api_key="API Key" api_key="API Key"

View File

@ -25,7 +25,7 @@ pub struct ConnectorAuthentication {
pub fiserv: Option<SignatureKey>, pub fiserv: Option<SignatureKey>,
pub forte: Option<MultiAuthKey>, pub forte: Option<MultiAuthKey>,
pub globalpay: Option<BodyKey>, pub globalpay: Option<BodyKey>,
pub globepay: Option<HeaderKey>, pub globepay: Option<BodyKey>,
pub iatapay: Option<SignatureKey>, pub iatapay: Option<SignatureKey>,
pub mollie: Option<HeaderKey>, pub mollie: Option<HeaderKey>,
pub multisafepay: Option<HeaderKey>, pub multisafepay: Option<HeaderKey>,

View File

@ -1739,6 +1739,9 @@
} }
} }
}, },
"AliPay": {
"type": "object"
},
"AliPayHkRedirection": { "AliPayHkRedirection": {
"type": "object" "type": "object"
}, },
@ -2939,6 +2942,7 @@
"fiserv", "fiserv",
"forte", "forte",
"globalpay", "globalpay",
"globepay",
"klarna", "klarna",
"mollie", "mollie",
"multisafepay", "multisafepay",
@ -8535,6 +8539,17 @@
}, },
"WalletData": { "WalletData": {
"oneOf": [ "oneOf": [
{
"type": "object",
"required": [
"ali_pay"
],
"properties": {
"ali_pay": {
"$ref": "#/components/schemas/AliPay"
}
}
},
{ {
"type": "object", "type": "object",
"required": [ "required": [