mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-27 19:46:48 +08:00
feat(connector): [PayMe] Implement preprocessing flow for cards (#1904)
This commit is contained in:
committed by
GitHub
parent
5bc7592af3
commit
38b9c077b7
@ -2407,6 +2407,10 @@ pub struct ApplepaySessionTokenResponse {
|
||||
pub delayed_session_token: bool,
|
||||
/// The next action for the sdk (ex: calling confirm or sync call)
|
||||
pub sdk_next_action: SdkNextAction,
|
||||
/// The connector transaction id
|
||||
pub connector_reference_id: Option<String>,
|
||||
/// The public key id is to invoke third party sdk
|
||||
pub connector_sdk_public_key: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, serde::Serialize, Clone, ToSchema)]
|
||||
@ -2489,9 +2493,9 @@ pub struct ApplePayPaymentRequest {
|
||||
/// Represents the total for the payment.
|
||||
pub total: AmountInfo,
|
||||
/// The list of merchant capabilities(ex: whether capable of 3ds or no-3ds)
|
||||
pub merchant_capabilities: Vec<String>,
|
||||
pub merchant_capabilities: Option<Vec<String>>,
|
||||
/// The list of supported networks
|
||||
pub supported_networks: Vec<String>,
|
||||
pub supported_networks: Option<Vec<String>>,
|
||||
pub merchant_identifier: Option<String>,
|
||||
}
|
||||
|
||||
|
||||
@ -392,14 +392,18 @@ impl TryFrom<types::PaymentsSessionResponseRouterData<BluesnapWalletTokenRespons
|
||||
total_type: Some("final".to_string()),
|
||||
amount: item.data.request.amount.to_string(),
|
||||
},
|
||||
merchant_capabilities: applepay_metadata
|
||||
.data
|
||||
.payment_request_data
|
||||
.merchant_capabilities,
|
||||
supported_networks: applepay_metadata
|
||||
.data
|
||||
.payment_request_data
|
||||
.supported_networks,
|
||||
merchant_capabilities: Some(
|
||||
applepay_metadata
|
||||
.data
|
||||
.payment_request_data
|
||||
.merchant_capabilities,
|
||||
),
|
||||
supported_networks: Some(
|
||||
applepay_metadata
|
||||
.data
|
||||
.payment_request_data
|
||||
.supported_networks,
|
||||
),
|
||||
merchant_identifier: Some(
|
||||
applepay_metadata
|
||||
.data
|
||||
@ -414,6 +418,8 @@ impl TryFrom<types::PaymentsSessionResponseRouterData<BluesnapWalletTokenRespons
|
||||
next_action: payments::NextActionCall::Confirm,
|
||||
}
|
||||
},
|
||||
connector_reference_id: None,
|
||||
connector_sdk_public_key: None,
|
||||
},
|
||||
)),
|
||||
}),
|
||||
|
||||
@ -9,11 +9,8 @@ use transformers as payme;
|
||||
|
||||
use crate::{
|
||||
configs::settings,
|
||||
core::{
|
||||
errors::{self, CustomResult},
|
||||
payments,
|
||||
},
|
||||
headers, routes,
|
||||
core::errors::{self, CustomResult},
|
||||
headers,
|
||||
services::{self, request, ConnectorIntegration},
|
||||
types::{
|
||||
self,
|
||||
@ -86,12 +83,18 @@ impl ConnectorCommon for Payme {
|
||||
res.response
|
||||
.parse_struct("PaymeErrorResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
|
||||
let status_code = match res.status_code {
|
||||
500..=511 => 200,
|
||||
_ => res.status_code,
|
||||
};
|
||||
Ok(ErrorResponse {
|
||||
status_code: res.status_code,
|
||||
code: response.code,
|
||||
message: response.message,
|
||||
reason: response.reason,
|
||||
status_code,
|
||||
code: response.status_error_code.to_string(),
|
||||
message: response.status_error_details.clone(),
|
||||
reason: Some(format!(
|
||||
"{}, additional info: {}",
|
||||
response.status_error_details, response.status_additional_info
|
||||
)),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -101,26 +104,18 @@ impl ConnectorIntegration<api::Session, types::PaymentsSessionData, types::Payme
|
||||
{
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::AccessTokenAuth, types::AccessTokenRequestData, types::AccessToken>
|
||||
for Payme
|
||||
{
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::Verify, types::VerifyRequestData, types::PaymentsResponseData>
|
||||
for Payme
|
||||
{
|
||||
}
|
||||
impl api::PaymentsPreProcessing for Payme {}
|
||||
|
||||
impl
|
||||
ConnectorIntegration<
|
||||
api::InitPayment,
|
||||
types::PaymentsAuthorizeData,
|
||||
api::PreProcessing,
|
||||
types::PaymentsPreProcessingData,
|
||||
types::PaymentsResponseData,
|
||||
> for Payme
|
||||
{
|
||||
fn get_headers(
|
||||
&self,
|
||||
req: &types::PaymentsInitRouterData,
|
||||
req: &types::PaymentsPreProcessingRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
|
||||
self.build_headers(req, connectors)
|
||||
@ -132,11 +127,7 @@ impl
|
||||
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &types::RouterData<
|
||||
api::InitPayment,
|
||||
types::PaymentsAuthorizeData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
_req: &types::PaymentsPreProcessingRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Ok(format!("{}api/generate-sale", self.base_url(connectors)))
|
||||
@ -144,7 +135,7 @@ impl
|
||||
|
||||
fn get_request_body(
|
||||
&self,
|
||||
req: &types::PaymentsInitRouterData,
|
||||
req: &types::PaymentsPreProcessingRouterData,
|
||||
) -> CustomResult<Option<types::RequestBody>, errors::ConnectorError> {
|
||||
let req_obj = payme::GenerateSaleRequest::try_from(req)?;
|
||||
let payme_req = types::RequestBody::log_and_get_request_body(
|
||||
@ -157,37 +148,32 @@ impl
|
||||
|
||||
fn build_request(
|
||||
&self,
|
||||
req: &types::PaymentsInitRouterData,
|
||||
req: &types::PaymentsPreProcessingRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
Ok(Some(
|
||||
let req = Some(
|
||||
services::RequestBuilder::new()
|
||||
.method(services::Method::Post)
|
||||
.url(&types::PaymentsInitType::get_url(self, req, connectors)?)
|
||||
.attach_default_headers()
|
||||
.headers(types::PaymentsInitType::get_headers(self, req, connectors)?)
|
||||
.body(types::PaymentsInitType::get_request_body(self, req)?)
|
||||
.headers(types::PaymentsPreProcessingType::get_headers(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.url(&types::PaymentsPreProcessingType::get_url(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.body(types::PaymentsPreProcessingType::get_request_body(
|
||||
self, req,
|
||||
)?)
|
||||
.build(),
|
||||
))
|
||||
);
|
||||
Ok(req)
|
||||
}
|
||||
|
||||
fn handle_response(
|
||||
&self,
|
||||
data: &types::PaymentsInitRouterData,
|
||||
data: &types::PaymentsPreProcessingRouterData,
|
||||
res: Response,
|
||||
) -> CustomResult<
|
||||
types::RouterData<
|
||||
api::InitPayment,
|
||||
types::PaymentsAuthorizeData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
errors::ConnectorError,
|
||||
>
|
||||
where
|
||||
api::InitPayment: Clone,
|
||||
types::PaymentsAuthorizeData: Clone,
|
||||
types::PaymentsResponseData: Clone,
|
||||
{
|
||||
) -> CustomResult<types::PaymentsPreProcessingRouterData, errors::ConnectorError> {
|
||||
let response: payme::GenerateSaleResponse = res
|
||||
.response
|
||||
.parse_struct("Payme GenerateSaleResponse")
|
||||
@ -205,44 +191,29 @@ impl
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
|
||||
fn get_5xx_error_response(
|
||||
&self,
|
||||
res: Response,
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
// we are always getting 500 in error scenarios
|
||||
self.build_error_response(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::AccessTokenAuth, types::AccessTokenRequestData, types::AccessToken>
|
||||
for Payme
|
||||
{
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::Verify, types::VerifyRequestData, types::PaymentsResponseData>
|
||||
for Payme
|
||||
{
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::PaymentsResponseData>
|
||||
for Payme
|
||||
{
|
||||
async fn execute_pretasks(
|
||||
&self,
|
||||
router_data: &mut types::PaymentsAuthorizeRouterData,
|
||||
app_state: &routes::AppState,
|
||||
) -> CustomResult<(), errors::ConnectorError> {
|
||||
if router_data.request.mandate_id.is_none() {
|
||||
let integ: Box<
|
||||
&(dyn ConnectorIntegration<
|
||||
api::InitPayment,
|
||||
types::PaymentsAuthorizeData,
|
||||
types::PaymentsResponseData,
|
||||
> + Send
|
||||
+ Sync
|
||||
+ 'static),
|
||||
> = Box::new(&Self);
|
||||
let init_data = &types::PaymentsInitRouterData::from((
|
||||
&router_data.to_owned(),
|
||||
router_data.request.clone(),
|
||||
));
|
||||
let init_res = services::execute_connector_processing_step(
|
||||
app_state,
|
||||
integ,
|
||||
init_data,
|
||||
payments::CallConnectorAction::Trigger,
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
router_data.request.related_transaction_id = init_res.request.related_transaction_id;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_headers(
|
||||
&self,
|
||||
req: &types::PaymentsAuthorizeRouterData,
|
||||
@ -324,6 +295,14 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
|
||||
fn get_5xx_error_response(
|
||||
&self,
|
||||
res: Response,
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
// we are always getting 500 in error scenarios
|
||||
self.build_error_response(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsResponseData>
|
||||
@ -401,6 +380,14 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe
|
||||
http_code: res.status_code,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_5xx_error_response(
|
||||
&self,
|
||||
res: Response,
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
// we are always getting 500 in error scenarios
|
||||
self.build_error_response(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::PaymentsResponseData>
|
||||
@ -479,6 +466,14 @@ impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::Payme
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
|
||||
fn get_5xx_error_response(
|
||||
&self,
|
||||
res: Response,
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
// we are always getting 500 in error scenarios
|
||||
self.build_error_response(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::Void, types::PaymentsCancelData, types::PaymentsResponseData>
|
||||
@ -570,6 +565,14 @@ impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsRespon
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
|
||||
fn get_5xx_error_response(
|
||||
&self,
|
||||
res: Response,
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
// we are always getting 500 in error scenarios
|
||||
self.build_error_response(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponseData> for Payme {
|
||||
@ -651,6 +654,14 @@ impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponse
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
|
||||
fn get_5xx_error_response(
|
||||
&self,
|
||||
res: Response,
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
// we are always getting 500 in error scenarios
|
||||
self.build_error_response(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
|
||||
@ -6,13 +6,15 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
connector::utils::{
|
||||
missing_field_err, AddressDetailsData, CardData, PaymentsAuthorizeRequestData,
|
||||
PaymentsSyncRequestData, RouterData,
|
||||
self, missing_field_err, AddressDetailsData, CardData, PaymentsAuthorizeRequestData,
|
||||
PaymentsPreProcessingData, PaymentsSyncRequestData, RouterData,
|
||||
},
|
||||
core::errors,
|
||||
types::{self, api, storage::enums, MandateReference},
|
||||
};
|
||||
|
||||
const LANGUAGE: &str = "en";
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct PayRequest {
|
||||
buyer_name: Secret<String>,
|
||||
@ -20,6 +22,7 @@ pub struct PayRequest {
|
||||
payme_sale_id: String,
|
||||
#[serde(flatten)]
|
||||
card: PaymeCard,
|
||||
language: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
@ -32,6 +35,7 @@ pub struct MandateRequest {
|
||||
seller_payme_id: Secret<String>,
|
||||
sale_callback_url: String,
|
||||
buyer_key: Secret<String>,
|
||||
language: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
@ -71,6 +75,7 @@ pub struct GenerateSaleRequest {
|
||||
seller_payme_id: Secret<String>,
|
||||
sale_callback_url: String,
|
||||
sale_payment_method: SalePaymentMethod,
|
||||
language: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
@ -183,11 +188,12 @@ pub enum SaleType {
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum SalePaymentMethod {
|
||||
CreditCard,
|
||||
ApplePay,
|
||||
}
|
||||
|
||||
impl TryFrom<&types::PaymentsInitRouterData> for GenerateSaleRequest {
|
||||
impl TryFrom<&types::PaymentsPreProcessingRouterData> for GenerateSaleRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &types::PaymentsInitRouterData) -> Result<Self, Self::Error> {
|
||||
fn try_from(item: &types::PaymentsPreProcessingRouterData) -> Result<Self, Self::Error> {
|
||||
let sale_type = SaleType::try_from(item)?;
|
||||
let seller_payme_id = PaymeAuthType::try_from(&item.connector_auth_type)?.seller_payme_id;
|
||||
let order_details = item.request.get_order_details()?;
|
||||
@ -196,44 +202,67 @@ impl TryFrom<&types::PaymentsInitRouterData> for GenerateSaleRequest {
|
||||
.ok_or_else(missing_field_err("order_details"))?
|
||||
.product_name
|
||||
.clone();
|
||||
let pmd = item
|
||||
.request
|
||||
.payment_method_data
|
||||
.to_owned()
|
||||
.ok_or_else(missing_field_err("payment_method_data"))?;
|
||||
Ok(Self {
|
||||
currency: item.request.currency,
|
||||
sale_type,
|
||||
sale_price: item.request.amount,
|
||||
transaction_id: item.payment_id.clone(),
|
||||
product_name,
|
||||
sale_return_url: item.request.get_return_url()?,
|
||||
seller_payme_id,
|
||||
sale_price: item.request.get_amount()?,
|
||||
currency: item.request.get_currency()?,
|
||||
product_name,
|
||||
sale_payment_method: SalePaymentMethod::try_from(&pmd)?,
|
||||
sale_type,
|
||||
transaction_id: item.payment_id.clone(),
|
||||
sale_return_url: item.request.get_return_url()?,
|
||||
sale_callback_url: item.request.get_webhook_url()?,
|
||||
sale_payment_method: SalePaymentMethod::try_from(&item.request.payment_method_data)?,
|
||||
language: LANGUAGE.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&types::PaymentsInitRouterData> for SaleType {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(value: &types::PaymentsInitRouterData) -> Result<Self, Self::Error> {
|
||||
let sale_type = if value.request.setup_mandate_details.is_some() {
|
||||
// First mandate
|
||||
Self::Token
|
||||
} else {
|
||||
// Normal payments
|
||||
match value.request.is_auto_capture()? {
|
||||
true => Self::Sale,
|
||||
false => Self::Authorize,
|
||||
}
|
||||
};
|
||||
Ok(sale_type)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&PaymentMethodData> for SalePaymentMethod {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &PaymentMethodData) -> Result<Self, Self::Error> {
|
||||
match item {
|
||||
PaymentMethodData::Card(_) => Ok(Self::CreditCard),
|
||||
PaymentMethodData::Wallet(_)
|
||||
| PaymentMethodData::PayLater(_)
|
||||
PaymentMethodData::Wallet(wallet_data) => match wallet_data {
|
||||
api_models::payments::WalletData::ApplePay(_) => Ok(Self::ApplePay),
|
||||
api_models::payments::WalletData::AliPayQr(_)
|
||||
| api_models::payments::WalletData::AliPayRedirect(_)
|
||||
| api_models::payments::WalletData::AliPayHkRedirect(_)
|
||||
| api_models::payments::WalletData::MomoRedirect(_)
|
||||
| api_models::payments::WalletData::KakaoPayRedirect(_)
|
||||
| api_models::payments::WalletData::GoPayRedirect(_)
|
||||
| api_models::payments::WalletData::GcashRedirect(_)
|
||||
| api_models::payments::WalletData::ApplePayRedirect(_)
|
||||
| api_models::payments::WalletData::ApplePayThirdPartySdk(_)
|
||||
| api_models::payments::WalletData::DanaRedirect {}
|
||||
| api_models::payments::WalletData::GooglePay(_)
|
||||
| api_models::payments::WalletData::GooglePayRedirect(_)
|
||||
| api_models::payments::WalletData::GooglePayThirdPartySdk(_)
|
||||
| api_models::payments::WalletData::MbWayRedirect(_)
|
||||
| api_models::payments::WalletData::MobilePayRedirect(_)
|
||||
| api_models::payments::WalletData::PaypalRedirect(_)
|
||||
| api_models::payments::WalletData::PaypalSdk(_)
|
||||
| api_models::payments::WalletData::SamsungPay(_)
|
||||
| api_models::payments::WalletData::TwintRedirect {}
|
||||
| api_models::payments::WalletData::VippsRedirect {}
|
||||
| api_models::payments::WalletData::TouchNGoRedirect(_)
|
||||
| api_models::payments::WalletData::WeChatPayRedirect(_)
|
||||
| api_models::payments::WalletData::WeChatPayQr(_)
|
||||
| api_models::payments::WalletData::CashappQr(_)
|
||||
| api_models::payments::WalletData::SwishQr(_) => {
|
||||
Err(errors::ConnectorError::NotSupported {
|
||||
message: "Wallet".to_string(),
|
||||
connector: "payme",
|
||||
payment_experience: "Redirection".to_string(),
|
||||
}
|
||||
.into())
|
||||
}
|
||||
},
|
||||
PaymentMethodData::PayLater(_)
|
||||
| PaymentMethodData::BankRedirect(_)
|
||||
| PaymentMethodData::BankDebit(_)
|
||||
| PaymentMethodData::BankTransfer(_)
|
||||
@ -288,6 +317,81 @@ impl TryFrom<&types::RefundSyncRouterData> for PaymeQueryTransactionRequest {
|
||||
}
|
||||
}
|
||||
|
||||
impl<F>
|
||||
TryFrom<
|
||||
types::ResponseRouterData<
|
||||
F,
|
||||
GenerateSaleResponse,
|
||||
types::PaymentsPreProcessingData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
> for types::RouterData<F, types::PaymentsPreProcessingData, types::PaymentsResponseData>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: types::ResponseRouterData<
|
||||
F,
|
||||
GenerateSaleResponse,
|
||||
types::PaymentsPreProcessingData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let currency_code = item.data.request.get_currency()?;
|
||||
let amount = item.data.request.get_amount()?;
|
||||
let amount_in_base_unit = utils::to_currency_base_unit(amount, currency_code)?;
|
||||
let pmd = item.data.request.payment_method_data.to_owned();
|
||||
|
||||
let session_token = match pmd {
|
||||
Some(PaymentMethodData::Wallet(
|
||||
api_models::payments::WalletData::ApplePayThirdPartySdk(_),
|
||||
)) => Some(api_models::payments::SessionToken::ApplePay(Box::new(
|
||||
api_models::payments::ApplepaySessionTokenResponse {
|
||||
session_token_data:
|
||||
api_models::payments::ApplePaySessionResponse::NoSessionResponse,
|
||||
payment_request_data: Some(api_models::payments::ApplePayPaymentRequest {
|
||||
country_code: item.data.get_billing_country()?,
|
||||
currency_code,
|
||||
total: api_models::payments::AmountInfo {
|
||||
label: "Apple Pay".to_string(),
|
||||
total_type: None,
|
||||
amount: amount_in_base_unit,
|
||||
},
|
||||
merchant_capabilities: None,
|
||||
supported_networks: None,
|
||||
merchant_identifier: None,
|
||||
}),
|
||||
connector: "payme".to_string(),
|
||||
delayed_session_token: true,
|
||||
sdk_next_action: api_models::payments::SdkNextAction {
|
||||
next_action: api_models::payments::NextActionCall::Sync,
|
||||
},
|
||||
connector_reference_id: Some(item.response.payme_sale_id.to_owned()),
|
||||
connector_sdk_public_key: Some(
|
||||
PaymeAuthType::try_from(&item.data.connector_auth_type)?
|
||||
.payme_public_key
|
||||
.expose(),
|
||||
),
|
||||
},
|
||||
))),
|
||||
_ => None,
|
||||
};
|
||||
Ok(Self {
|
||||
// We don't get any status from payme, so defaulting it to pending
|
||||
status: enums::AttemptStatus::Pending,
|
||||
preprocessing_id: Some(item.response.payme_sale_id.to_owned()),
|
||||
response: Ok(types::PaymentsResponseData::PreProcessingResponse {
|
||||
pre_processing_id: types::PreprocessingResponseId::ConnectorTransactionId(
|
||||
item.response.payme_sale_id,
|
||||
),
|
||||
connector_metadata: None,
|
||||
session_token,
|
||||
connector_response_reference_id: None,
|
||||
}),
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&types::PaymentsAuthorizeRouterData> for MandateRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> {
|
||||
@ -307,6 +411,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for MandateRequest {
|
||||
seller_payme_id,
|
||||
sale_callback_url: item.request.get_webhook_url()?,
|
||||
buyer_key: Secret::new(item.request.get_connector_mandate_id()?),
|
||||
language: LANGUAGE.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -324,7 +429,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for PayRequest {
|
||||
};
|
||||
let buyer_email = item.request.get_email()?;
|
||||
let buyer_name = item.get_billing_address()?.get_full_name()?;
|
||||
let payme_sale_id = item.request.related_transaction_id.clone().ok_or(
|
||||
let payme_sale_id = item.preprocessing_id.to_owned().ok_or(
|
||||
errors::ConnectorError::MissingConnectorRelatedTransactionID {
|
||||
id: "payme_sale_id".to_string(),
|
||||
},
|
||||
@ -334,6 +439,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for PayRequest {
|
||||
buyer_email,
|
||||
buyer_name,
|
||||
payme_sale_id,
|
||||
language: LANGUAGE.to_string(),
|
||||
})
|
||||
}
|
||||
_ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()),
|
||||
@ -344,7 +450,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for PayRequest {
|
||||
// Auth Struct
|
||||
pub struct PaymeAuthType {
|
||||
#[allow(dead_code)]
|
||||
pub(super) payme_client_key: Secret<String>,
|
||||
pub(super) payme_public_key: Secret<String>,
|
||||
pub(super) seller_payme_id: Secret<String>,
|
||||
}
|
||||
|
||||
@ -354,13 +460,30 @@ impl TryFrom<&types::ConnectorAuthType> for PaymeAuthType {
|
||||
match auth_type {
|
||||
types::ConnectorAuthType::BodyKey { api_key, key1 } => Ok(Self {
|
||||
seller_payme_id: api_key.to_owned(),
|
||||
payme_client_key: key1.to_owned(),
|
||||
payme_public_key: key1.to_owned(),
|
||||
}),
|
||||
_ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&types::PaymentsPreProcessingRouterData> for SaleType {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(value: &types::PaymentsPreProcessingRouterData) -> Result<Self, Self::Error> {
|
||||
let sale_type = if value.request.setup_mandate_details.is_some() {
|
||||
// First mandate
|
||||
Self::Token
|
||||
} else {
|
||||
// Normal payments
|
||||
match value.request.is_auto_capture()? {
|
||||
true => Self::Sale,
|
||||
false => Self::Authorize,
|
||||
}
|
||||
};
|
||||
Ok(sale_type)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum SaleStatus {
|
||||
@ -420,44 +543,6 @@ pub struct PaymeMetadata {
|
||||
payme_transaction_id: String,
|
||||
}
|
||||
|
||||
impl<F>
|
||||
TryFrom<
|
||||
types::ResponseRouterData<
|
||||
F,
|
||||
GenerateSaleResponse,
|
||||
types::PaymentsAuthorizeData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
> for types::RouterData<F, types::PaymentsAuthorizeData, types::PaymentsResponseData>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: types::ResponseRouterData<
|
||||
F,
|
||||
GenerateSaleResponse,
|
||||
types::PaymentsAuthorizeData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
status: enums::AttemptStatus::Authorizing,
|
||||
request: types::PaymentsAuthorizeData {
|
||||
related_transaction_id: Some(item.response.payme_sale_id.clone()),
|
||||
..item.data.request
|
||||
},
|
||||
response: Ok(types::PaymentsResponseData::TransactionResponse {
|
||||
resource_id: types::ResponseId::ConnectorTransactionId(item.response.payme_sale_id),
|
||||
redirection_data: None,
|
||||
mandate_reference: None,
|
||||
connector_metadata: None,
|
||||
network_txn_id: None,
|
||||
connector_response_reference_id: None,
|
||||
}),
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct PaymentCaptureRequest {
|
||||
payme_sale_id: String,
|
||||
@ -481,6 +566,7 @@ pub struct PaymeRefundRequest {
|
||||
sale_refund_amount: i64,
|
||||
payme_sale_id: String,
|
||||
seller_payme_id: Secret<String>,
|
||||
language: String,
|
||||
}
|
||||
|
||||
impl<F> TryFrom<&types::RefundsRouterData<F>> for PaymeRefundRequest {
|
||||
@ -491,6 +577,7 @@ impl<F> TryFrom<&types::RefundsRouterData<F>> for PaymeRefundRequest {
|
||||
payme_sale_id: item.request.connector_transaction_id.clone(),
|
||||
seller_payme_id: auth_type.seller_payme_id,
|
||||
sale_refund_amount: item.request.refund_amount,
|
||||
language: LANGUAGE.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -579,9 +666,9 @@ impl<F, T>
|
||||
#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct PaymeErrorResponse {
|
||||
pub status_code: u16,
|
||||
pub code: String,
|
||||
pub message: String,
|
||||
pub reason: Option<String>,
|
||||
pub status_error_details: String,
|
||||
pub status_additional_info: String,
|
||||
pub status_error_code: u16,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
|
||||
@ -1048,8 +1048,10 @@ pub fn get_apple_pay_session<F, T>(
|
||||
payment_request_data: Some(api_models::payments::ApplePayPaymentRequest {
|
||||
country_code: apple_pay_init_result.country_code,
|
||||
currency_code: apple_pay_init_result.currency_code,
|
||||
supported_networks: apple_pay_init_result.supported_networks.clone(),
|
||||
merchant_capabilities: apple_pay_init_result.merchant_capabilities.clone(),
|
||||
supported_networks: Some(apple_pay_init_result.supported_networks.clone()),
|
||||
merchant_capabilities: Some(
|
||||
apple_pay_init_result.merchant_capabilities.clone(),
|
||||
),
|
||||
total: apple_pay_init_result.total.into(),
|
||||
merchant_identifier: None,
|
||||
}),
|
||||
@ -1060,6 +1062,8 @@ pub fn get_apple_pay_session<F, T>(
|
||||
next_action: api_models::payments::NextActionCall::Sync,
|
||||
}
|
||||
},
|
||||
connector_reference_id: None,
|
||||
connector_sdk_public_key: None,
|
||||
},
|
||||
))),
|
||||
connector_response_reference_id: None,
|
||||
|
||||
@ -193,6 +193,10 @@ pub trait PaymentsPreProcessingData {
|
||||
fn get_payment_method_type(&self) -> Result<diesel_models::enums::PaymentMethodType, Error>;
|
||||
fn get_currency(&self) -> Result<diesel_models::enums::Currency, Error>;
|
||||
fn get_amount(&self) -> Result<i64, Error>;
|
||||
fn is_auto_capture(&self) -> Result<bool, Error>;
|
||||
fn get_order_details(&self) -> Result<Vec<OrderDetailsWithAmount>, Error>;
|
||||
fn get_webhook_url(&self) -> Result<String, Error>;
|
||||
fn get_return_url(&self) -> Result<String, Error>;
|
||||
}
|
||||
|
||||
impl PaymentsPreProcessingData for types::PaymentsPreProcessingData {
|
||||
@ -210,6 +214,28 @@ impl PaymentsPreProcessingData for types::PaymentsPreProcessingData {
|
||||
fn get_amount(&self) -> Result<i64, Error> {
|
||||
self.amount.ok_or_else(missing_field_err("amount"))
|
||||
}
|
||||
fn is_auto_capture(&self) -> Result<bool, Error> {
|
||||
match self.capture_method {
|
||||
Some(diesel_models::enums::CaptureMethod::Automatic) | None => Ok(true),
|
||||
Some(diesel_models::enums::CaptureMethod::Manual) => Ok(false),
|
||||
Some(_) => Err(errors::ConnectorError::CaptureMethodNotSupported.into()),
|
||||
}
|
||||
}
|
||||
fn get_order_details(&self) -> Result<Vec<OrderDetailsWithAmount>, Error> {
|
||||
self.order_details
|
||||
.clone()
|
||||
.ok_or_else(missing_field_err("order_details"))
|
||||
}
|
||||
fn get_webhook_url(&self) -> Result<String, Error> {
|
||||
self.webhook_url
|
||||
.clone()
|
||||
.ok_or_else(missing_field_err("webhook_url"))
|
||||
}
|
||||
fn get_return_url(&self) -> Result<String, Error> {
|
||||
self.router_return_url
|
||||
.clone()
|
||||
.ok_or_else(missing_field_err("return_url"))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PaymentsAuthorizeRequestData {
|
||||
|
||||
@ -843,7 +843,7 @@ async fn complete_preprocessing_steps_if_required<F, Req>(
|
||||
state: &AppState,
|
||||
connector: &api::ConnectorData,
|
||||
payment_data: &PaymentData<F>,
|
||||
router_data: types::RouterData<F, Req, types::PaymentsResponseData>,
|
||||
mut router_data: types::RouterData<F, Req, types::PaymentsResponseData>,
|
||||
should_continue_payment: bool,
|
||||
) -> RouterResult<(types::RouterData<F, Req, types::PaymentsResponseData>, bool)>
|
||||
where
|
||||
@ -871,7 +871,7 @@ where
|
||||
_ => (router_data, should_continue_payment),
|
||||
},
|
||||
Some(api_models::payments::PaymentMethodData::Wallet(_)) => {
|
||||
if connector.connector_name.to_string() == *"trustpay" {
|
||||
if is_preprocessing_required_for_wallets(connector.connector_name.to_string()) {
|
||||
(
|
||||
router_data.preprocessing_steps(state, connector).await?,
|
||||
false,
|
||||
@ -880,12 +880,27 @@ where
|
||||
(router_data, should_continue_payment)
|
||||
}
|
||||
}
|
||||
Some(api_models::payments::PaymentMethodData::Card(_)) => {
|
||||
if connector.connector_name == types::Connector::Payme {
|
||||
router_data = router_data.preprocessing_steps(state, connector).await?;
|
||||
|
||||
let is_error_in_response = router_data.response.is_err();
|
||||
// If is_error_in_response is true, should_continue_payment should be false, we should throw the error
|
||||
(router_data, !is_error_in_response)
|
||||
} else {
|
||||
(router_data, should_continue_payment)
|
||||
}
|
||||
}
|
||||
_ => (router_data, should_continue_payment),
|
||||
};
|
||||
|
||||
Ok(router_data_and_should_continue_payment)
|
||||
}
|
||||
|
||||
pub fn is_preprocessing_required_for_wallets(connector_name: String) -> bool {
|
||||
connector_name == *"trustpay" || connector_name == *"payme"
|
||||
}
|
||||
|
||||
fn is_payment_method_tokenization_enabled_for_connector(
|
||||
state: &AppState,
|
||||
connector_name: &str,
|
||||
|
||||
@ -745,7 +745,6 @@ default_imp_for_pre_processing_steps!(
|
||||
connector::Opennode,
|
||||
connector::Payeezy,
|
||||
connector::Paypal,
|
||||
connector::Payme,
|
||||
connector::Payu,
|
||||
connector::Powertranz,
|
||||
connector::Rapyd,
|
||||
|
||||
@ -360,6 +360,11 @@ impl TryFrom<types::PaymentsAuthorizeData> for types::PaymentsPreProcessingData
|
||||
email: data.email,
|
||||
currency: Some(data.currency),
|
||||
payment_method_type: data.payment_method_type,
|
||||
setup_mandate_details: data.setup_mandate_details,
|
||||
capture_method: data.capture_method,
|
||||
order_details: data.order_details,
|
||||
router_return_url: data.router_return_url,
|
||||
webhook_url: data.webhook_url,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -192,14 +192,18 @@ async fn create_applepay_session_token(
|
||||
})?,
|
||||
currency_code: router_data.request.currency,
|
||||
total: amount_info,
|
||||
merchant_capabilities: applepay_metadata
|
||||
.data
|
||||
.payment_request_data
|
||||
.merchant_capabilities,
|
||||
supported_networks: applepay_metadata
|
||||
.data
|
||||
.payment_request_data
|
||||
.supported_networks,
|
||||
merchant_capabilities: Some(
|
||||
applepay_metadata
|
||||
.data
|
||||
.payment_request_data
|
||||
.merchant_capabilities,
|
||||
),
|
||||
supported_networks: Some(
|
||||
applepay_metadata
|
||||
.data
|
||||
.payment_request_data
|
||||
.supported_networks,
|
||||
),
|
||||
merchant_identifier: Some(
|
||||
applepay_metadata
|
||||
.data
|
||||
@ -267,6 +271,8 @@ fn create_apple_pay_session_response(
|
||||
connector: connector_name,
|
||||
delayed_session_token: delayed_response,
|
||||
sdk_next_action: { payment_types::SdkNextAction { next_action } },
|
||||
connector_reference_id: None,
|
||||
connector_sdk_public_key: None,
|
||||
},
|
||||
)),
|
||||
}),
|
||||
|
||||
@ -2449,7 +2449,7 @@ pub fn router_data_type_conversion<F1, F2, Req1, Req2, Res1, Res2>(
|
||||
attempt_id: router_data.attempt_id,
|
||||
access_token: router_data.access_token,
|
||||
session_token: router_data.session_token,
|
||||
reference_id: None,
|
||||
reference_id: router_data.reference_id,
|
||||
payment_method_token: router_data.payment_method_token,
|
||||
customer_id: router_data.customer_id,
|
||||
connector_customer: router_data.connector_customer,
|
||||
|
||||
@ -614,7 +614,9 @@ where
|
||||
payment_attempt
|
||||
.connector
|
||||
.as_ref()
|
||||
.map(|connector| matches!(connector.as_str(), "trustpay"))
|
||||
.map(|connector| {
|
||||
matches!(connector.as_str(), "trustpay") || matches!(connector.as_str(), "payme")
|
||||
})
|
||||
.and_then(|is_connector_supports_third_party_sdk| {
|
||||
if is_connector_supports_third_party_sdk {
|
||||
payment_attempt
|
||||
@ -1100,6 +1102,39 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::PaymentsPreProce
|
||||
fn try_from(additional_data: PaymentAdditionalData<'_, F>) -> Result<Self, Self::Error> {
|
||||
let payment_data = additional_data.payment_data;
|
||||
let payment_method_data = payment_data.payment_method_data;
|
||||
let router_base_url = &additional_data.router_base_url;
|
||||
let attempt = &payment_data.payment_attempt;
|
||||
let connector_name = &additional_data.connector_name;
|
||||
|
||||
let order_details = payment_data
|
||||
.payment_intent
|
||||
.order_details
|
||||
.map(|order_details| {
|
||||
order_details
|
||||
.iter()
|
||||
.map(|data| {
|
||||
data.to_owned()
|
||||
.parse_value("OrderDetailsWithAmount")
|
||||
.change_context(errors::ApiErrorResponse::InvalidDataValue {
|
||||
field_name: "OrderDetailsWithAmount",
|
||||
})
|
||||
.attach_printable("Unable to parse OrderDetailsWithAmount")
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let webhook_url = Some(helpers::create_webhook_url(
|
||||
router_base_url,
|
||||
&attempt.merchant_id,
|
||||
connector_name,
|
||||
));
|
||||
let router_return_url = Some(helpers::create_redirect_url(
|
||||
router_base_url,
|
||||
attempt,
|
||||
connector_name,
|
||||
payment_data.creds_identifier.as_deref(),
|
||||
));
|
||||
|
||||
Ok(Self {
|
||||
payment_method_data,
|
||||
@ -1107,6 +1142,11 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::PaymentsPreProce
|
||||
currency: Some(payment_data.currency),
|
||||
amount: Some(payment_data.amount.into()),
|
||||
payment_method_type: payment_data.payment_attempt.payment_method_type,
|
||||
setup_mandate_details: payment_data.setup_mandate,
|
||||
capture_method: payment_data.payment_attempt.capture_method,
|
||||
order_details,
|
||||
router_return_url,
|
||||
webhook_url,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -364,6 +364,11 @@ pub struct PaymentsPreProcessingData {
|
||||
pub email: Option<Email>,
|
||||
pub currency: Option<storage_enums::Currency>,
|
||||
pub payment_method_type: Option<storage_enums::PaymentMethodType>,
|
||||
pub setup_mandate_details: Option<payments::MandateData>,
|
||||
pub capture_method: Option<storage_enums::CaptureMethod>,
|
||||
pub order_details: Option<Vec<api_models::payments::OrderDetailsWithAmount>>,
|
||||
pub router_return_url: Option<String>,
|
||||
pub webhook_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
||||
Reference in New Issue
Block a user