mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-02 12:06:56 +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,
|
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,
|
||||||
|
|||||||
@ -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 {}
|
||||||
|
|
||||||
|
|||||||
@ -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 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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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>,
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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)),
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
@ -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>,
|
||||||
|
|||||||
@ -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": [
|
||||||
|
|||||||
Reference in New Issue
Block a user