mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-03 05:17:02 +08:00
feat: support gpay and applepay session response for all connectors (#839)
Co-authored-by: Arun Raj M <jarnura47@gmail.com>
This commit is contained in:
committed by
GitHub
parent
465933ba72
commit
d23e14c57a
@ -1,273 +0,0 @@
|
||||
mod transformers;
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
use common_utils::ext_traits::ValueExt;
|
||||
use error_stack::{IntoReport, ResultExt};
|
||||
|
||||
use self::transformers as applepay;
|
||||
use crate::{
|
||||
configs::settings,
|
||||
core::errors::{self, CustomResult},
|
||||
headers, services,
|
||||
types::{
|
||||
self,
|
||||
api::{self, ConnectorCommon},
|
||||
},
|
||||
utils::{self, BytesExt, OptionExt},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Applepay;
|
||||
|
||||
impl ConnectorCommon for Applepay {
|
||||
fn id(&self) -> &'static str {
|
||||
"applepay"
|
||||
}
|
||||
|
||||
fn base_url<'a>(&self, connectors: &'a settings::Connectors) -> &'a str {
|
||||
connectors.applepay.base_url.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl api::Payment for Applepay {}
|
||||
impl api::PaymentAuthorize for Applepay {}
|
||||
impl api::PaymentSync for Applepay {}
|
||||
impl api::PaymentVoid for Applepay {}
|
||||
impl api::PaymentCapture for Applepay {}
|
||||
impl api::PreVerify for Applepay {}
|
||||
impl api::PaymentSession for Applepay {}
|
||||
impl api::ConnectorAccessToken for Applepay {}
|
||||
impl api::PaymentToken for Applepay {}
|
||||
|
||||
impl
|
||||
services::ConnectorIntegration<
|
||||
api::PaymentMethodToken,
|
||||
types::PaymentMethodTokenizationData,
|
||||
types::PaymentsResponseData,
|
||||
> for Applepay
|
||||
{
|
||||
// Not Implemented (R)
|
||||
}
|
||||
|
||||
impl
|
||||
services::ConnectorIntegration<
|
||||
api::AccessTokenAuth,
|
||||
types::AccessTokenRequestData,
|
||||
types::AccessToken,
|
||||
> for Applepay
|
||||
{
|
||||
// Not Implemented (R)
|
||||
}
|
||||
|
||||
impl
|
||||
services::ConnectorIntegration<
|
||||
api::Verify,
|
||||
types::VerifyRequestData,
|
||||
types::PaymentsResponseData,
|
||||
> for Applepay
|
||||
{
|
||||
}
|
||||
|
||||
impl
|
||||
services::ConnectorIntegration<
|
||||
api::Capture,
|
||||
types::PaymentsCaptureData,
|
||||
types::PaymentsResponseData,
|
||||
> for Applepay
|
||||
{
|
||||
}
|
||||
|
||||
impl
|
||||
services::ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsResponseData>
|
||||
for Applepay
|
||||
{
|
||||
}
|
||||
|
||||
impl
|
||||
services::ConnectorIntegration<
|
||||
api::Authorize,
|
||||
types::PaymentsAuthorizeData,
|
||||
types::PaymentsResponseData,
|
||||
> for Applepay
|
||||
{
|
||||
}
|
||||
|
||||
impl
|
||||
services::ConnectorIntegration<
|
||||
api::Void,
|
||||
types::PaymentsCancelData,
|
||||
types::PaymentsResponseData,
|
||||
> for Applepay
|
||||
{
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl
|
||||
services::ConnectorIntegration<
|
||||
api::Session,
|
||||
types::PaymentsSessionData,
|
||||
types::PaymentsResponseData,
|
||||
> for Applepay
|
||||
{
|
||||
fn get_headers(
|
||||
&self,
|
||||
_req: &types::PaymentsSessionRouterData,
|
||||
_connectors: &settings::Connectors,
|
||||
) -> CustomResult<Vec<(String, String)>, errors::ConnectorError> {
|
||||
let header = vec![(
|
||||
headers::CONTENT_TYPE.to_string(),
|
||||
types::PaymentsSessionType::get_content_type(self).to_string(),
|
||||
)];
|
||||
Ok(header)
|
||||
}
|
||||
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &types::PaymentsSessionRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Ok(format!(
|
||||
"{}{}",
|
||||
self.base_url(connectors),
|
||||
"paymentservices/paymentSession"
|
||||
))
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
&self,
|
||||
req: &types::PaymentsSessionRouterData,
|
||||
) -> CustomResult<Option<String>, errors::ConnectorError> {
|
||||
let connector_req = applepay::ApplepaySessionRequest::try_from(req)?;
|
||||
let req = utils::Encode::<applepay::ApplepaySessionRequest>::encode_to_string_of_json(
|
||||
&connector_req,
|
||||
)
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
Ok(Some(req))
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
&self,
|
||||
req: &types::PaymentsSessionRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
let request = services::RequestBuilder::new()
|
||||
.method(services::Method::Post)
|
||||
.url(&types::PaymentsSessionType::get_url(self, req, connectors)?)
|
||||
.attach_default_headers()
|
||||
.headers(types::PaymentsSessionType::get_headers(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.body(types::PaymentsSessionType::get_request_body(self, req)?)
|
||||
.add_certificate(types::PaymentsSessionType::get_certificate(self, req)?)
|
||||
.add_certificate_key(types::PaymentsSessionType::get_certificate_key(self, req)?)
|
||||
.build();
|
||||
Ok(Some(request))
|
||||
}
|
||||
|
||||
fn handle_response(
|
||||
&self,
|
||||
data: &types::PaymentsSessionRouterData,
|
||||
res: types::Response,
|
||||
) -> CustomResult<types::PaymentsSessionRouterData, errors::ConnectorError> {
|
||||
let response: applepay::ApplepaySessionTokenResponse = res
|
||||
.response
|
||||
.parse_struct("ApplepaySessionResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
|
||||
types::RouterData::try_from(types::ResponseRouterData {
|
||||
response,
|
||||
data: data.clone(),
|
||||
http_code: res.status_code,
|
||||
})
|
||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)
|
||||
}
|
||||
|
||||
fn get_error_response(
|
||||
&self,
|
||||
res: types::Response,
|
||||
) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
|
||||
let response: applepay::ErrorResponse = res
|
||||
.response
|
||||
.parse_struct("ErrorResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
Ok(types::ErrorResponse {
|
||||
status_code: res.status_code,
|
||||
code: response.status_code,
|
||||
message: response.status_message,
|
||||
reason: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_certificate(
|
||||
&self,
|
||||
req: &types::PaymentsSessionRouterData,
|
||||
) -> CustomResult<Option<String>, errors::ConnectorError> {
|
||||
let metadata = req
|
||||
.connector_meta_data
|
||||
.to_owned()
|
||||
.get_required_value("connector_meta_data")
|
||||
.change_context(errors::ConnectorError::NoConnectorMetaData)?;
|
||||
|
||||
let metadata: transformers::ApplePayMetadata = metadata
|
||||
.parse_value("ApplePayMetaData")
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
|
||||
Ok(Some(metadata.session_token_data.certificate))
|
||||
}
|
||||
|
||||
fn get_certificate_key(
|
||||
&self,
|
||||
req: &types::PaymentsSessionRouterData,
|
||||
) -> CustomResult<Option<String>, errors::ConnectorError> {
|
||||
let metadata = req
|
||||
.connector_meta_data
|
||||
.to_owned()
|
||||
.get_required_value("connector_meta_data")
|
||||
.change_context(errors::ConnectorError::NoConnectorMetaData)?;
|
||||
|
||||
let metadata: transformers::ApplePayMetadata = metadata
|
||||
.parse_value("ApplePayMetaData")
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
|
||||
Ok(Some(metadata.session_token_data.certificate_keys))
|
||||
}
|
||||
}
|
||||
|
||||
impl api::Refund for Applepay {}
|
||||
impl api::RefundExecute for Applepay {}
|
||||
impl api::RefundSync for Applepay {}
|
||||
|
||||
impl services::ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsResponseData>
|
||||
for Applepay
|
||||
{
|
||||
}
|
||||
|
||||
impl services::ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponseData>
|
||||
for Applepay
|
||||
{
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl api::IncomingWebhook for Applepay {
|
||||
fn get_webhook_object_reference_id(
|
||||
&self,
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
fn get_webhook_event_type(
|
||||
&self,
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<api::IncomingWebhookEvent, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
fn get_webhook_resource_object(
|
||||
&self,
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<serde_json::Value, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
}
|
||||
@ -1,228 +0,0 @@
|
||||
use api_models::payments;
|
||||
use common_utils::ext_traits::ValueExt;
|
||||
use error_stack::ResultExt;
|
||||
use masking::{Deserialize, Serialize};
|
||||
|
||||
use crate::{connector::utils, core::errors, types, utils::OptionExt};
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ApplepaySessionRequest {
|
||||
merchant_identifier: String,
|
||||
display_name: String,
|
||||
initiative: String,
|
||||
initiative_context: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ApplepaySessionTokenResponse {
|
||||
pub epoch_timestamp: u64,
|
||||
pub expires_at: u64,
|
||||
pub merchant_session_identifier: String,
|
||||
pub nonce: String,
|
||||
pub merchant_identifier: String,
|
||||
pub domain_name: String,
|
||||
pub display_name: String,
|
||||
pub signature: String,
|
||||
pub operational_analytics_identifier: String,
|
||||
pub retries: u8,
|
||||
pub psp_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ErrorResponse {
|
||||
pub status_code: String,
|
||||
pub status_message: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
pub struct ApplePayMetadata {
|
||||
pub payment_request_data: PaymentRequestMetadata,
|
||||
pub session_token_data: SessionRequest,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
pub struct PaymentRequestMetadata {
|
||||
pub supported_networks: Vec<String>,
|
||||
pub merchant_capabilities: Vec<String>,
|
||||
pub label: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
pub struct SessionRequest {
|
||||
pub certificate: String,
|
||||
pub certificate_keys: String,
|
||||
pub merchant_identifier: String,
|
||||
pub display_name: String,
|
||||
pub initiative: String,
|
||||
pub initiative_context: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct PaymentRequest {
|
||||
pub apple_pay_merchant_id: String,
|
||||
pub country_code: api_models::enums::CountryCode,
|
||||
pub currency_code: String,
|
||||
pub total: AmountInfo,
|
||||
pub merchant_capabilities: Vec<String>,
|
||||
pub supported_networks: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
pub struct AmountInfo {
|
||||
pub label: String,
|
||||
#[serde(rename = "type")]
|
||||
pub label_type: String,
|
||||
pub amount: String,
|
||||
}
|
||||
|
||||
impl TryFrom<&types::PaymentsSessionRouterData> for ApplepaySessionRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &types::PaymentsSessionRouterData) -> Result<Self, Self::Error> {
|
||||
let metadata = item
|
||||
.connector_meta_data
|
||||
.to_owned()
|
||||
.get_required_value("connector_meta_data")
|
||||
.change_context(errors::ConnectorError::NoConnectorMetaData)?;
|
||||
|
||||
let metadata: ApplePayMetadata = metadata
|
||||
.parse_value("ApplePayMetadata")
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
|
||||
Ok(Self {
|
||||
merchant_identifier: metadata.session_token_data.merchant_identifier,
|
||||
display_name: metadata.session_token_data.display_name,
|
||||
initiative: metadata.session_token_data.initiative,
|
||||
initiative_context: metadata.session_token_data.initiative_context,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<F>
|
||||
TryFrom<
|
||||
types::ResponseRouterData<
|
||||
F,
|
||||
ApplepaySessionTokenResponse,
|
||||
types::PaymentsSessionData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
> for types::RouterData<F, types::PaymentsSessionData, types::PaymentsResponseData>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: types::ResponseRouterData<
|
||||
F,
|
||||
ApplepaySessionTokenResponse,
|
||||
types::PaymentsSessionData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let metadata = item
|
||||
.data
|
||||
.connector_meta_data
|
||||
.to_owned()
|
||||
.get_required_value("connector_meta_data")
|
||||
.change_context(errors::ConnectorError::NoConnectorMetaData)?;
|
||||
|
||||
let metadata: ApplePayMetadata = metadata
|
||||
.parse_value("ApplePayMetadata")
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
|
||||
let amount_info = AmountInfo {
|
||||
label: metadata.payment_request_data.label,
|
||||
label_type: "final".to_string(),
|
||||
amount: utils::to_currency_base_unit(
|
||||
item.data.request.amount,
|
||||
item.data.request.currency,
|
||||
)?,
|
||||
};
|
||||
|
||||
let payment_request = PaymentRequest {
|
||||
country_code: item
|
||||
.data
|
||||
.request
|
||||
.country
|
||||
.to_owned()
|
||||
.get_required_value("country_code")
|
||||
.change_context(errors::ConnectorError::MissingRequiredField {
|
||||
field_name: "country_code",
|
||||
})?,
|
||||
currency_code: item.data.request.currency.to_string(),
|
||||
total: amount_info,
|
||||
merchant_capabilities: metadata.payment_request_data.merchant_capabilities,
|
||||
supported_networks: metadata.payment_request_data.supported_networks,
|
||||
apple_pay_merchant_id: metadata.session_token_data.merchant_identifier,
|
||||
};
|
||||
|
||||
let applepay_session = ApplepaySessionTokenResponse {
|
||||
epoch_timestamp: item.response.epoch_timestamp,
|
||||
expires_at: item.response.expires_at,
|
||||
merchant_session_identifier: item.response.merchant_session_identifier,
|
||||
nonce: item.response.nonce,
|
||||
merchant_identifier: item.response.merchant_identifier,
|
||||
domain_name: item.response.domain_name,
|
||||
display_name: item.response.display_name,
|
||||
signature: item.response.signature,
|
||||
operational_analytics_identifier: item.response.operational_analytics_identifier,
|
||||
retries: item.response.retries,
|
||||
psp_id: item.response.psp_id,
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
response: Ok(types::PaymentsResponseData::SessionResponse {
|
||||
session_token: {
|
||||
api_models::payments::SessionToken::ApplePay(Box::new(
|
||||
payments::ApplepaySessionTokenResponse {
|
||||
session_token_data: applepay_session.into(),
|
||||
payment_request_data: payment_request.into(),
|
||||
},
|
||||
))
|
||||
},
|
||||
}),
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PaymentRequest> for payments::ApplePayPaymentRequest {
|
||||
fn from(value: PaymentRequest) -> Self {
|
||||
Self {
|
||||
country_code: value.country_code,
|
||||
currency_code: value.currency_code,
|
||||
total: value.total.into(),
|
||||
merchant_capabilities: value.merchant_capabilities,
|
||||
supported_networks: value.supported_networks,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AmountInfo> for payments::AmountInfo {
|
||||
fn from(value: AmountInfo) -> Self {
|
||||
Self {
|
||||
label: value.label,
|
||||
total_type: value.label_type,
|
||||
amount: value.amount,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ApplepaySessionTokenResponse> for payments::ApplePaySessionResponse {
|
||||
fn from(value: ApplepaySessionTokenResponse) -> Self {
|
||||
Self {
|
||||
epoch_timestamp: value.epoch_timestamp,
|
||||
expires_at: value.expires_at,
|
||||
merchant_session_identifier: value.merchant_session_identifier,
|
||||
nonce: value.nonce,
|
||||
merchant_identifier: value.merchant_identifier,
|
||||
domain_name: value.domain_name,
|
||||
display_name: value.display_name,
|
||||
signature: value.signature,
|
||||
operational_analytics_identifier: value.operational_analytics_identifier,
|
||||
retries: value.retries,
|
||||
psp_id: value.psp_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user