mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-30 09:38:33 +08:00
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:
@ -642,7 +642,7 @@ pub enum Connector {
|
||||
Fiserv,
|
||||
Forte,
|
||||
Globalpay,
|
||||
// Globepay, added as template code for future usage
|
||||
Globepay,
|
||||
Klarna,
|
||||
Mollie,
|
||||
Multisafepay,
|
||||
@ -747,7 +747,7 @@ pub enum RoutableConnectors {
|
||||
Fiserv,
|
||||
Forte,
|
||||
Globalpay,
|
||||
// Globepay, added as template code for future usage
|
||||
Globepay,
|
||||
Iatapay,
|
||||
Klarna,
|
||||
Mollie,
|
||||
|
||||
@ -895,6 +895,8 @@ pub struct BankDebitBilling {
|
||||
#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum WalletData {
|
||||
/// The wallet data for Ali Pay QrCode
|
||||
AliPay(Box<AliPay>),
|
||||
/// The wallet data for Ali Pay redirect
|
||||
AliPayRedirect(AliPayRedirection),
|
||||
/// 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)]
|
||||
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)]
|
||||
pub struct AliPayRedirection {}
|
||||
|
||||
|
||||
@ -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,
|
||||
}],
|
||||
)]),
|
||||
},
|
||||
),
|
||||
])),
|
||||
),
|
||||
(
|
||||
|
||||
@ -2,22 +2,24 @@ mod transformers;
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
use common_utils::crypto::{self, GenerateDigest};
|
||||
use error_stack::{IntoReport, ResultExt};
|
||||
use hex::encode;
|
||||
use masking::ExposeInterface;
|
||||
use rand::distributions::DistString;
|
||||
use time::OffsetDateTime;
|
||||
use transformers as globepay;
|
||||
|
||||
use crate::{
|
||||
configs::settings,
|
||||
consts,
|
||||
core::errors::{self, CustomResult},
|
||||
headers,
|
||||
services::{
|
||||
self,
|
||||
request::{self, Mask},
|
||||
ConnectorIntegration,
|
||||
},
|
||||
services::{self, request, ConnectorIntegration},
|
||||
types::{
|
||||
self,
|
||||
api::{self, ConnectorCommon, ConnectorCommonExt},
|
||||
storage::enums,
|
||||
ErrorResponse, Response,
|
||||
},
|
||||
utils::{self, BytesExt},
|
||||
@ -55,21 +57,44 @@ where
|
||||
{
|
||||
fn build_headers(
|
||||
&self,
|
||||
req: &types::RouterData<Flow, Request, Response>,
|
||||
_req: &types::RouterData<Flow, Request, Response>,
|
||||
_connectors: &settings::Connectors,
|
||||
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
|
||||
let mut header = vec![(
|
||||
let header = vec![(
|
||||
headers::CONTENT_TYPE.to_string(),
|
||||
types::PaymentsAuthorizeType::get_content_type(self)
|
||||
.to_string()
|
||||
.into(),
|
||||
self.get_content_type().to_string().into(),
|
||||
)];
|
||||
let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
|
||||
header.append(&mut api_key);
|
||||
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 {
|
||||
fn id(&self) -> &'static str {
|
||||
"globepay"
|
||||
@ -83,18 +108,6 @@ impl ConnectorCommon for Globepay {
|
||||
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(
|
||||
&self,
|
||||
res: Response,
|
||||
@ -106,9 +119,9 @@ impl ConnectorCommon for Globepay {
|
||||
|
||||
Ok(ErrorResponse {
|
||||
status_code: res.status_code,
|
||||
code: response.code,
|
||||
message: response.message,
|
||||
reason: response.reason,
|
||||
code: response.return_code.to_string(),
|
||||
message: consts::NO_ERROR_MESSAGE.to_string(),
|
||||
reason: Some(response.return_msg),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -116,7 +129,6 @@ impl ConnectorCommon for Globepay {
|
||||
impl ConnectorIntegration<api::Session, types::PaymentsSessionData, types::PaymentsResponseData>
|
||||
for Globepay
|
||||
{
|
||||
//TODO: implement sessions flow
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::AccessTokenAuth, types::AccessTokenRequestData, types::AccessToken>
|
||||
@ -146,10 +158,24 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
|
||||
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &types::PaymentsAuthorizeRouterData,
|
||||
_connectors: &settings::Connectors,
|
||||
req: &types::PaymentsAuthorizeRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> 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(
|
||||
@ -172,7 +198,7 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
Ok(Some(
|
||||
services::RequestBuilder::new()
|
||||
.method(services::Method::Post)
|
||||
.method(services::Method::Put)
|
||||
.url(&types::PaymentsAuthorizeType::get_url(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
@ -226,10 +252,16 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe
|
||||
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &types::PaymentsSyncRouterData,
|
||||
_connectors: &settings::Connectors,
|
||||
req: &types::PaymentsSyncRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> 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(
|
||||
@ -252,7 +284,7 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe
|
||||
data: &types::PaymentsSyncRouterData,
|
||||
res: Response,
|
||||
) -> CustomResult<types::PaymentsSyncRouterData, errors::ConnectorError> {
|
||||
let response: globepay::GlobepayPaymentsResponse = res
|
||||
let response: globepay::GlobepaySyncResponse = res
|
||||
.response
|
||||
.parse_struct("globepay PaymentsSyncResponse")
|
||||
.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>
|
||||
for Globepay
|
||||
{
|
||||
fn get_headers(
|
||||
&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(
|
||||
fn build_request(
|
||||
&self,
|
||||
_req: &types::PaymentsCaptureRouterData,
|
||||
_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> {
|
||||
Ok(Some(
|
||||
services::RequestBuilder::new()
|
||||
.method(services::Method::Post)
|
||||
.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(),
|
||||
))
|
||||
Err(errors::ConnectorError::FlowNotSupported {
|
||||
flow: "Manual Capture".to_owned(),
|
||||
connector: "Globepay".to_owned(),
|
||||
}
|
||||
|
||||
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)
|
||||
.into())
|
||||
}
|
||||
}
|
||||
|
||||
@ -351,138 +327,22 @@ impl ConnectorIntegration<api::Void, types::PaymentsCancelData, types::PaymentsR
|
||||
impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsResponseData>
|
||||
for Globepay
|
||||
{
|
||||
fn get_headers(
|
||||
&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(
|
||||
fn build_request(
|
||||
&self,
|
||||
_req: &types::RefundsRouterData<api::Execute>,
|
||||
_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> {
|
||||
let request = services::RequestBuilder::new()
|
||||
.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)
|
||||
Err(errors::ConnectorError::NotImplemented("Refund".to_string()).into())
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponseData> for Globepay {
|
||||
fn get_headers(
|
||||
&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(
|
||||
fn build_request(
|
||||
&self,
|
||||
_req: &types::RefundSyncRouterData,
|
||||
_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> {
|
||||
Ok(Some(
|
||||
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)
|
||||
Err(errors::ConnectorError::NotImplemented("Refund Sync".to_string()).into())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,90 +1,115 @@
|
||||
use error_stack::ResultExt;
|
||||
use masking::Secret;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
connector::utils::PaymentsAuthorizeRequestData,
|
||||
connector::utils::RouterData,
|
||||
consts,
|
||||
core::errors,
|
||||
types::{self, api, storage::enums},
|
||||
};
|
||||
|
||||
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct GlobepayPaymentsRequest {
|
||||
amount: i64,
|
||||
card: GlobepayCard,
|
||||
price: i64,
|
||||
description: String,
|
||||
currency: enums::Currency,
|
||||
channel: GlobepayChannel,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||
pub struct GlobepayCard {
|
||||
name: Secret<String>,
|
||||
number: cards::CardNumber,
|
||||
expiry_month: Secret<String>,
|
||||
expiry_year: Secret<String>,
|
||||
cvc: Secret<String>,
|
||||
complete: bool,
|
||||
#[derive(Debug, Serialize)]
|
||||
pub enum GlobepayChannel {
|
||||
Alipay,
|
||||
Wechat,
|
||||
}
|
||||
|
||||
impl TryFrom<&types::PaymentsAuthorizeRouterData> for GlobepayPaymentsRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> {
|
||||
match item.request.payment_method_data.clone() {
|
||||
api::PaymentMethodData::Card(req_card) => {
|
||||
let card = GlobepayCard {
|
||||
name: req_card.card_holder_name,
|
||||
number: req_card.card_number,
|
||||
expiry_month: req_card.card_exp_month,
|
||||
expiry_year: req_card.card_exp_year,
|
||||
cvc: req_card.card_cvc,
|
||||
complete: item.request.is_auto_capture()?,
|
||||
let channel: GlobepayChannel = match &item.request.payment_method_data {
|
||||
api::PaymentMethodData::Wallet(ref wallet_data) => match wallet_data {
|
||||
api::WalletData::AliPay(_) => GlobepayChannel::Alipay,
|
||||
api::WalletData::WeChatPay(_) => GlobepayChannel::Wechat,
|
||||
_ => Err(errors::ConnectorError::NotImplemented(
|
||||
"Payment method".to_string(),
|
||||
))?,
|
||||
},
|
||||
_ => Err(errors::ConnectorError::NotImplemented(
|
||||
"Payment method".to_string(),
|
||||
))?,
|
||||
};
|
||||
let description = item.get_description()?;
|
||||
Ok(Self {
|
||||
amount: item.request.amount,
|
||||
card,
|
||||
price: item.request.amount,
|
||||
description,
|
||||
currency: item.request.currency,
|
||||
channel,
|
||||
})
|
||||
}
|
||||
_ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
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: Secret::new(api_key.to_string()),
|
||||
types::ConnectorAuthType::BodyKey { api_key, key1 } => Ok(Self {
|
||||
partner_code: Secret::new(api_key.to_owned()),
|
||||
credential_code: Secret::new(key1.to_owned()),
|
||||
}),
|
||||
_ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
pub enum GlobepayPaymentStatus {
|
||||
Succeeded,
|
||||
Failed,
|
||||
#[default]
|
||||
Processing,
|
||||
Success,
|
||||
Exists,
|
||||
}
|
||||
|
||||
impl From<GlobepayPaymentStatus> for enums::AttemptStatus {
|
||||
fn from(item: GlobepayPaymentStatus) -> Self {
|
||||
match item {
|
||||
GlobepayPaymentStatus::Succeeded => Self::Charged,
|
||||
GlobepayPaymentStatus::Failed => Self::Failure,
|
||||
GlobepayPaymentStatus::Processing => Self::Authorizing,
|
||||
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::Exists => Self::Failure,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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 {
|
||||
status: GlobepayPaymentStatus,
|
||||
id: String,
|
||||
result_code: Option<GlobepayPaymentStatus>,
|
||||
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>
|
||||
@ -100,10 +125,104 @@ impl<F, T>
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
) -> 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 {
|
||||
status: enums::AttemptStatus::from(item.response.status),
|
||||
status: enums::AttemptStatus::from(globepay_status),
|
||||
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,
|
||||
mandate_reference: None,
|
||||
connector_metadata: None,
|
||||
@ -112,10 +231,34 @@ impl<F, T>
|
||||
}),
|
||||
..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 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 status_code: u16,
|
||||
pub code: String,
|
||||
pub return_msg: String,
|
||||
pub return_code: GlobepayReturnCode,
|
||||
pub message: String,
|
||||
pub reason: Option<String>,
|
||||
}
|
||||
|
||||
@ -165,6 +165,7 @@ Never share your secret api keys. Keep them guarded and secure.
|
||||
api_models::disputes::DisputeResponsePaymentsRetrieve,
|
||||
api_models::payments::AddressDetails,
|
||||
api_models::payments::BankDebitData,
|
||||
api_models::payments::AliPay,
|
||||
api_models::payments::AliPayRedirection,
|
||||
api_models::payments::AliPayHkRedirection,
|
||||
api_models::payments::MbWayRedirection,
|
||||
|
||||
@ -232,6 +232,7 @@ impl ConnectorData {
|
||||
enums::Connector::Fiserv => Ok(Box::new(&connector::Fiserv)),
|
||||
enums::Connector::Forte => Ok(Box::new(&connector::Forte)),
|
||||
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::Klarna => Ok(Box::new(&connector::Klarna)),
|
||||
enums::Connector::Mollie => Ok(Box::new(&connector::Mollie)),
|
||||
|
||||
@ -144,8 +144,8 @@ api_key="Classic PMT API Key"
|
||||
key1 = "Evoucher PMT API Key"
|
||||
|
||||
[globepay]
|
||||
api_key="API Key"
|
||||
|
||||
api_key = "Partner code"
|
||||
key1 = "Credential code"
|
||||
|
||||
[powertranz]
|
||||
api_key="API Key"
|
||||
|
||||
@ -25,7 +25,7 @@ pub struct ConnectorAuthentication {
|
||||
pub fiserv: Option<SignatureKey>,
|
||||
pub forte: Option<MultiAuthKey>,
|
||||
pub globalpay: Option<BodyKey>,
|
||||
pub globepay: Option<HeaderKey>,
|
||||
pub globepay: Option<BodyKey>,
|
||||
pub iatapay: Option<SignatureKey>,
|
||||
pub mollie: Option<HeaderKey>,
|
||||
pub multisafepay: Option<HeaderKey>,
|
||||
|
||||
@ -1739,6 +1739,9 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"AliPay": {
|
||||
"type": "object"
|
||||
},
|
||||
"AliPayHkRedirection": {
|
||||
"type": "object"
|
||||
},
|
||||
@ -2939,6 +2942,7 @@
|
||||
"fiserv",
|
||||
"forte",
|
||||
"globalpay",
|
||||
"globepay",
|
||||
"klarna",
|
||||
"mollie",
|
||||
"multisafepay",
|
||||
@ -8535,6 +8539,17 @@
|
||||
},
|
||||
"WalletData": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ali_pay"
|
||||
],
|
||||
"properties": {
|
||||
"ali_pay": {
|
||||
"$ref": "#/components/schemas/AliPay"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
||||
Reference in New Issue
Block a user