feat: applepay payment request object (#519)

This commit is contained in:
Sangamesh Kulkarni
2023-02-23 14:56:30 +05:30
committed by GitHub
parent 63f9b6124d
commit 8ee097ea21
8 changed files with 1001 additions and 372 deletions

View File

@ -1096,51 +1096,100 @@ pub struct GpaySessionTokenData {
#[serde(rename_all = "lowercase")]
pub enum SessionToken {
/// The session response structure for Google Pay
Gpay {
/// The merchant info
merchant_info: GpayMerchantInfo,
/// List of the allowed payment meythods
allowed_payment_methods: Vec<GpayAllowedPaymentMethods>,
/// The transaction info Google Pay requires
transaction_info: GpayTransactionInfo,
},
Gpay(Box<GpayData>),
/// The session response structure for Klarna
Klarna {
/// The session token for Klarna
session_token: String,
/// The identifier for the session
session_id: String,
},
Klarna(Box<KlarnaData>),
/// The session response structure for PayPal
Paypal {
/// The session token for PayPal
session_token: String,
},
Paypal(Box<PaypalData>),
/// The session response structure for Apple Pay
Applepay {
/// Timestamp at which session is requested
epoch_timestamp: u64,
/// Timestamp at which session expires
expires_at: u64,
/// The identifier for the merchant session
merchant_session_identifier: String,
/// Applepay generates unique ID (UUID) value
nonce: String,
/// The identifier for the merchant
merchant_identifier: String,
/// The domain name of the merchant which is registered in Apple Pay
domain_name: String,
/// The name to be displayed on Apple Pay button
display_name: String,
/// A string which represents the properties of a payment
signature: String,
/// The identifier for the operational analytics
operational_analytics_identifier: String,
/// The number of retries to get the session response
retries: u8,
/// The identifier for the connector transaction
psp_id: String,
},
Applepay(Box<ApplepayData>),
}
#[derive(Debug, Clone, serde::Serialize, ToSchema)]
#[serde(rename_all = "lowercase")]
pub struct GpayData {
/// The merchant info
pub merchant_info: GpayMerchantInfo,
/// List of the allowed payment meythods
pub allowed_payment_methods: Vec<GpayAllowedPaymentMethods>,
/// The transaction info Google Pay requires
pub transaction_info: GpayTransactionInfo,
}
#[derive(Debug, Clone, serde::Serialize, ToSchema)]
#[serde(rename_all = "lowercase")]
pub struct KlarnaData {
/// The session token for Klarna
pub session_token: String,
/// The identifier for the session
pub session_id: String,
}
#[derive(Debug, Clone, serde::Serialize, ToSchema)]
#[serde(rename_all = "lowercase")]
pub struct PaypalData {
/// The session token for PayPal
pub session_token: String,
}
#[derive(Debug, Clone, serde::Serialize, ToSchema)]
#[serde(rename_all = "lowercase")]
pub struct ApplepayData {
/// Session object for Apple Pay
pub session_object: ApplePaySessionObject,
/// Payment request object for Apple Pay
pub payment_request_object: ApplePayRequest,
}
#[derive(Debug, Clone, serde::Serialize, ToSchema, serde::Deserialize)]
pub struct ApplePaySessionObject {
/// Timestamp at which session is requested
pub epoch_timestamp: u64,
/// Timestamp at which session expires
pub expires_at: u64,
/// The identifier for the merchant session
pub merchant_session_identifier: String,
/// Applepay generates unique ID (UUID) value
pub nonce: String,
/// The identifier for the merchant
pub merchant_identifier: String,
/// The domain name of the merchant which is registered in Apple Pay
pub domain_name: String,
/// The name to be displayed on Apple Pay button
pub display_name: String,
/// A string which represents the properties of a payment
pub signature: String,
/// The identifier for the operational analytics
pub operational_analytics_identifier: String,
/// The number of retries to get the session response
pub retries: u8,
/// The identifier for the connector transaction
pub psp_id: String,
}
#[derive(Debug, Clone, serde::Serialize, ToSchema, serde::Deserialize)]
pub struct ApplePayRequest {
/// The code for country
pub country_code: String,
/// The code for currency
pub currency_code: String,
/// 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>,
/// The list of supported networks
pub supported_networks: Vec<String>,
}
#[derive(Debug, Clone, serde::Serialize, ToSchema, serde::Deserialize)]
pub struct AmountInfo {
/// the label must be non-empty to pass validation.
pub label: String,
/// The type of label
#[serde(rename = "type")]
pub label_type: String,
/// The total amount for the payment
pub amount: String,
}
#[derive(Default, Debug, serde::Serialize, Clone, ToSchema)]

View File

@ -197,11 +197,11 @@ impl
.get_required_value("connector_meta_data")
.change_context(errors::ConnectorError::NoConnectorMetaData)?;
let session_object: transformers::SessionObject = metadata
.parse_value("SessionObject")
let metadata: transformers::ApplePayMetaData = metadata
.parse_value("ApplePayMetaData")
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
Ok(Some(session_object.certificate))
Ok(Some(metadata.session_object.certificate))
}
fn get_certificate_key(
@ -214,11 +214,11 @@ impl
.get_required_value("connector_meta_data")
.change_context(errors::ConnectorError::NoConnectorMetaData)?;
let session_object: transformers::SessionObject = metadata
.parse_value("SessionObject")
let metadata: transformers::ApplePayMetaData = metadata
.parse_value("ApplePayMetaData")
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
Ok(Some(session_object.certificate_keys))
Ok(Some(metadata.session_object.certificate_keys))
}
}

View File

@ -1,3 +1,4 @@
use api_models::payments;
use common_utils::ext_traits::ValueExt;
use error_stack::ResultExt;
use masking::{Deserialize, Serialize};
@ -16,17 +17,17 @@ pub struct ApplepaySessionRequest {
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ApplepaySessionResponse {
epoch_timestamp: u64,
expires_at: u64,
merchant_session_identifier: String,
nonce: String,
merchant_identifier: String,
domain_name: String,
display_name: String,
signature: String,
operational_analytics_identifier: String,
retries: u8,
psp_id: String,
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)]
@ -36,6 +37,19 @@ pub struct ErrorResponse {
pub status_message: String,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct ApplePayMetaData {
pub payment_object: PaymentObjectMetaData,
pub session_object: SessionObject,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct PaymentObjectMetaData {
pub supported_networks: Vec<String>,
pub merchant_capabilities: Vec<String>,
pub label: String,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct SessionObject {
pub certificate: String,
@ -46,6 +60,25 @@ pub struct SessionObject {
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: String,
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> {
@ -55,48 +88,136 @@ impl TryFrom<&types::PaymentsSessionRouterData> for ApplepaySessionRequest {
.get_required_value("connector_meta_data")
.change_context(errors::ConnectorError::NoConnectorMetaData)?;
let session_object: SessionObject = metadata
.parse_value("SessionObject")
let metadata: ApplePayMetaData = metadata
.parse_value("ApplePayMetaData")
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
Ok(Self {
merchant_identifier: session_object.merchant_identifier,
display_name: session_object.display_name,
initiative: session_object.initiative,
initiative_context: session_object.initiative_context,
merchant_identifier: metadata.session_object.merchant_identifier,
display_name: metadata.session_object.display_name,
initiative: metadata.session_object.initiative,
initiative_context: metadata.session_object.initiative_context,
})
}
}
impl<F, T>
TryFrom<types::ResponseRouterData<F, ApplepaySessionResponse, T, types::PaymentsResponseData>>
for types::RouterData<F, T, types::PaymentsResponseData>
impl<F>
TryFrom<
types::ResponseRouterData<
F,
ApplepaySessionResponse,
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, ApplepaySessionResponse, T, types::PaymentsResponseData>,
item: types::ResponseRouterData<
F,
ApplepaySessionResponse,
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_object.label,
label_type: "final".to_string(),
amount: (item.data.request.amount / 100).to_string(),
};
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_object.merchant_capabilities,
supported_networks: metadata.payment_object.supported_networks,
apple_pay_merchant_id: metadata.session_object.merchant_identifier,
};
let applepay_session_object = ApplepaySessionResponse {
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 {
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,
}
api_models::payments::SessionToken::Applepay(Box::new(payments::ApplepayData {
session_object: applepay_session_object.into(),
payment_request_object: payment_request.into(),
}))
},
}),
..item.data
})
}
}
impl From<PaymentRequest> for payments::ApplePayRequest {
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,
label_type: value.label_type,
amount: value.amount,
}
}
}
impl From<ApplepaySessionResponse> for payments::ApplePaySessionObject {
fn from(value: ApplepaySessionResponse) -> 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,
}
}
}

View File

@ -1,3 +1,4 @@
use api_models::payments;
use base64::Engine;
use error_stack::ResultExt;
use serde::{Deserialize, Serialize};
@ -243,9 +244,9 @@ impl<F, T>
) -> Result<Self, Self::Error> {
Ok(Self {
response: Ok(types::PaymentsResponseData::SessionResponse {
session_token: types::api::SessionToken::Paypal {
session_token: types::api::SessionToken::Paypal(Box::new(payments::PaypalData {
session_token: item.response.client_token.value,
},
})),
}),
..item.data
})

View File

@ -1,3 +1,4 @@
use api_models::payments;
use error_stack::report;
use serde::{Deserialize, Serialize};
@ -71,10 +72,10 @@ impl TryFrom<types::PaymentsSessionResponseRouterData<KlarnaSessionResponse>>
let response = &item.response;
Ok(Self {
response: Ok(types::PaymentsResponseData::SessionResponse {
session_token: types::api::SessionToken::Klarna {
session_token: types::api::SessionToken::Klarna(Box::new(payments::KlarnaData {
session_token: response.client_token.clone(),
session_id: response.session_id.clone(),
},
})),
}),
..item.data
})

View File

@ -91,11 +91,11 @@ fn create_gpay_session_token(
let response_router_data = types::PaymentsSessionRouterData {
response: Ok(types::PaymentsResponseData::SessionResponse {
session_token: payment_types::SessionToken::Gpay {
transaction_info,
session_token: payment_types::SessionToken::Gpay(Box::new(payment_types::GpayData {
merchant_info: gpay_data.data.merchant_info,
allowed_payment_methods: gpay_data.data.allowed_payment_methods,
},
transaction_info,
})),
}),
..router_data.clone()
};

View File

@ -173,12 +173,19 @@ Never share your secret api keys. Keep them guarded and secure.
api_models::payments::PaymentsSessionRequest,
api_models::payments::PaymentsSessionResponse,
api_models::payments::SessionToken,
api_models::payments::ApplePaySessionObject,
api_models::payments::ApplePayRequest,
api_models::payments::AmountInfo,
api_models::payments::GpayMerchantInfo,
api_models::payments::GpayAllowedPaymentMethods,
api_models::payments::GpayAllowedMethodsParameters,
api_models::payments::GpayTokenizationSpecification,
api_models::payments::GpayTokenParameters,
api_models::payments::GpayTransactionInfo,
api_models::payments::GpayData,
api_models::payments::KlarnaData,
api_models::payments::PaypalData,
api_models::payments::ApplepayData,
api_models::payments::PaymentsCancelRequest,
api_models::payments::PaymentListConstraints,
api_models::payments::PaymentListResponse,

File diff suppressed because it is too large Load Diff