mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-02 04:04:43 +08:00
feat(router): verify service for applepay merchant registration (#2009)
This commit is contained in:
@ -40,6 +40,11 @@ master_enc_key = "73ad7bbbbc640c845a150f67d058b279849370cd2c1f3c67c4dd6c869213e1
|
||||
jwt_secret = "secret"
|
||||
recon_admin_api_key = "recon_test_admin"
|
||||
|
||||
[applepay_merchant_configs]
|
||||
merchant_cert_key = "MERCHANT CERTIFICATE KEY"
|
||||
merchant_cert = "MERCHANT CERTIFICATE"
|
||||
common_merchant_identifier = "COMMON MERCHANT IDENTIFIER"
|
||||
|
||||
[locker]
|
||||
host = ""
|
||||
mock_locker = true
|
||||
|
||||
@ -16,4 +16,5 @@ pub mod payments;
|
||||
#[cfg(feature = "payouts")]
|
||||
pub mod payouts;
|
||||
pub mod refunds;
|
||||
pub mod verifications;
|
||||
pub mod webhooks;
|
||||
|
||||
24
crates/api_models/src/verifications.rs
Normal file
24
crates/api_models/src/verifications.rs
Normal file
@ -0,0 +1,24 @@
|
||||
/// The request body for verification of merchant (everything except domain_names are prefilled)
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ApplepayMerchantVerificationConfigs {
|
||||
pub domain_names: Vec<String>,
|
||||
pub encrypt_to: String,
|
||||
pub partner_internal_merchant_identifier: String,
|
||||
pub partner_merchant_name: String,
|
||||
}
|
||||
|
||||
/// The derivation point for domain names from request body
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ApplepayMerchantVerificationRequest {
|
||||
pub domain_names: Vec<String>,
|
||||
}
|
||||
|
||||
/// Response to be sent for the verify/applepay api
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ApplepayMerchantResponse {
|
||||
pub status_message: String,
|
||||
pub status_code: String,
|
||||
}
|
||||
@ -94,6 +94,16 @@ pub struct Settings {
|
||||
#[cfg(feature = "payouts")]
|
||||
pub payouts: Payouts,
|
||||
pub multiple_api_version_supported_connectors: MultipleApiVersionSupportedConnectors,
|
||||
#[cfg(all(feature = "olap", feature = "kms"))]
|
||||
pub applepay_merchant_configs: ApplepayMerchantConfigs,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, Default)]
|
||||
#[serde(default)]
|
||||
pub struct ApplepayMerchantConfigs {
|
||||
pub merchant_cert: String,
|
||||
pub merchant_cert_key: String,
|
||||
pub common_merchant_identifier: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, Default)]
|
||||
|
||||
@ -138,6 +138,11 @@ pub fn mk_app(
|
||||
.service(routes::Disputes::server(state.clone()))
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "olap", feature = "kms"))]
|
||||
{
|
||||
server_app = server_app.service(routes::Verify::server(state.clone()));
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
{
|
||||
server_app = server_app.service(routes::Payouts::server(state.clone()));
|
||||
|
||||
@ -18,12 +18,16 @@ pub mod payments;
|
||||
#[cfg(feature = "payouts")]
|
||||
pub mod payouts;
|
||||
pub mod refunds;
|
||||
#[cfg(all(feature = "olap", feature = "kms"))]
|
||||
pub mod verification;
|
||||
pub mod webhooks;
|
||||
|
||||
#[cfg(feature = "dummy_connector")]
|
||||
pub use self::app::DummyConnector;
|
||||
#[cfg(feature = "payouts")]
|
||||
pub use self::app::Payouts;
|
||||
#[cfg(all(feature = "olap", feature = "kms"))]
|
||||
pub use self::app::Verify;
|
||||
pub use self::app::{
|
||||
ApiKeys, AppState, BusinessProfile, Cache, Cards, Configs, Customers, Disputes, EphemeralKey,
|
||||
Files, Health, Mandates, MerchantAccount, MerchantConnectorAccount, PaymentMethods, Payments,
|
||||
|
||||
@ -9,6 +9,8 @@ use tokio::sync::oneshot;
|
||||
use super::dummy_connector::*;
|
||||
#[cfg(feature = "payouts")]
|
||||
use super::payouts::*;
|
||||
#[cfg(all(feature = "olap", feature = "kms"))]
|
||||
use super::verification::apple_pay_merchant_registration;
|
||||
#[cfg(feature = "olap")]
|
||||
use super::{admin::*, api_keys::*, disputes::*, files::*};
|
||||
use super::{cache::*, health::*};
|
||||
@ -554,3 +556,18 @@ impl BusinessProfile {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "olap", feature = "kms"))]
|
||||
pub struct Verify;
|
||||
|
||||
#[cfg(all(feature = "olap", feature = "kms"))]
|
||||
impl Verify {
|
||||
pub fn server(state: AppState) -> Scope {
|
||||
web::scope("/verify")
|
||||
.app_data(web::Data::new(state))
|
||||
.service(
|
||||
web::resource("/{merchant_id}/apple_pay")
|
||||
.route(web::post().to(apple_pay_merchant_registration)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
32
crates/router/src/routes/verification.rs
Normal file
32
crates/router/src/routes/verification.rs
Normal file
@ -0,0 +1,32 @@
|
||||
use actix_web::{web, HttpRequest, Responder};
|
||||
#[cfg(all(feature = "olap", feature = "kms"))]
|
||||
use api_models::verifications;
|
||||
use router_env::{instrument, tracing, Flow};
|
||||
|
||||
use super::app::AppState;
|
||||
use crate::{
|
||||
services::{api, authentication as auth},
|
||||
utils::verification,
|
||||
};
|
||||
|
||||
#[instrument(skip_all, fields(flow = ?Flow::Verification))]
|
||||
pub async fn apple_pay_merchant_registration(
|
||||
state: web::Data<AppState>,
|
||||
req: HttpRequest,
|
||||
json_payload: web::Json<verifications::ApplepayMerchantVerificationRequest>,
|
||||
path: web::Path<String>,
|
||||
) -> impl Responder {
|
||||
let flow = Flow::Verification;
|
||||
let merchant_id = path.into_inner();
|
||||
api::server_wrap(
|
||||
flow,
|
||||
state.get_ref(),
|
||||
&req,
|
||||
json_payload,
|
||||
|state, _, body| {
|
||||
verification::verify_merchant_creds_for_applepay(state, &req, body, &state.conf.kms)
|
||||
},
|
||||
&auth::MerchantIdAuth(merchant_id.clone()),
|
||||
)
|
||||
.await
|
||||
}
|
||||
@ -1,6 +1,8 @@
|
||||
pub mod custom_serde;
|
||||
pub mod db_utils;
|
||||
pub mod ext_traits;
|
||||
#[cfg(all(feature = "olap", feature = "kms"))]
|
||||
pub mod verification;
|
||||
|
||||
#[cfg(feature = "kv_store")]
|
||||
pub mod storage_partitioning;
|
||||
|
||||
112
crates/router/src/utils/verification.rs
Normal file
112
crates/router/src/utils/verification.rs
Normal file
@ -0,0 +1,112 @@
|
||||
use actix_web::web;
|
||||
#[cfg(all(feature = "olap", feature = "kms"))]
|
||||
use api_models::verifications::{self, ApplepayMerchantResponse};
|
||||
use error_stack::{Report, ResultExt};
|
||||
#[cfg(feature = "kms")]
|
||||
use external_services::kms;
|
||||
|
||||
use crate::{
|
||||
core::errors::{self, api_error_response},
|
||||
headers, logger,
|
||||
routes::AppState,
|
||||
services, types, utils,
|
||||
};
|
||||
|
||||
const APPLEPAY_MERCHANT_VERIFICATION_URL: &str =
|
||||
"https://apple-pay-gateway.apple.com/paymentservices/registerMerchant";
|
||||
const APPLEPAY_INTERNAL_MERCHANT_NAME: &str = "Applepay_merchant";
|
||||
|
||||
pub async fn verify_merchant_creds_for_applepay(
|
||||
state: &AppState,
|
||||
_req: &actix_web::HttpRequest,
|
||||
body: web::Json<verifications::ApplepayMerchantVerificationRequest>,
|
||||
kms_config: &kms::KmsConfig,
|
||||
) -> common_utils::errors::CustomResult<
|
||||
services::ApplicationResponse<ApplepayMerchantResponse>,
|
||||
api_error_response::ApiErrorResponse,
|
||||
> {
|
||||
let encrypted_merchant_identifier = &state
|
||||
.conf
|
||||
.applepay_merchant_configs
|
||||
.common_merchant_identifier;
|
||||
let encrypted_cert = &state.conf.applepay_merchant_configs.merchant_cert;
|
||||
let encrypted_key = &state.conf.applepay_merchant_configs.merchant_cert_key;
|
||||
|
||||
let applepay_internal_merchant_identifier = kms::get_kms_client(kms_config)
|
||||
.await
|
||||
.decrypt(encrypted_merchant_identifier)
|
||||
.await
|
||||
.change_context(api_error_response::ApiErrorResponse::InternalServerError)?;
|
||||
|
||||
let cert_data = kms::get_kms_client(kms_config)
|
||||
.await
|
||||
.decrypt(encrypted_cert)
|
||||
.await
|
||||
.change_context(api_error_response::ApiErrorResponse::InternalServerError)?;
|
||||
|
||||
let key_data = kms::get_kms_client(kms_config)
|
||||
.await
|
||||
.decrypt(encrypted_key)
|
||||
.await
|
||||
.change_context(api_error_response::ApiErrorResponse::InternalServerError)?;
|
||||
|
||||
let request_body = verifications::ApplepayMerchantVerificationConfigs {
|
||||
domain_names: body.domain_names.clone(),
|
||||
encrypt_to: applepay_internal_merchant_identifier.to_string(),
|
||||
partner_internal_merchant_identifier: applepay_internal_merchant_identifier.to_string(),
|
||||
partner_merchant_name: APPLEPAY_INTERNAL_MERCHANT_NAME.to_string(),
|
||||
};
|
||||
|
||||
let applepay_req = types::RequestBody::log_and_get_request_body(
|
||||
&request_body,
|
||||
utils::Encode::<verifications::ApplepayMerchantVerificationRequest>::encode_to_string_of_json,
|
||||
)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to encode ApplePay session request to a string of json")?;
|
||||
|
||||
let apple_pay_merch_verification_req = services::RequestBuilder::new()
|
||||
.method(services::Method::Post)
|
||||
.url(APPLEPAY_MERCHANT_VERIFICATION_URL)
|
||||
.attach_default_headers()
|
||||
.headers(vec![(
|
||||
headers::CONTENT_TYPE.to_string(),
|
||||
"application/json".to_string().into(),
|
||||
)])
|
||||
.body(Some(applepay_req))
|
||||
.add_certificate(Some(cert_data))
|
||||
.add_certificate_key(Some(key_data))
|
||||
.build();
|
||||
|
||||
let response = services::call_connector_api(state, apple_pay_merch_verification_req).await;
|
||||
log_applepay_verification_response_if_error(&response);
|
||||
|
||||
let applepay_response =
|
||||
response.change_context(api_error_response::ApiErrorResponse::InternalServerError)?;
|
||||
|
||||
// Error is already logged
|
||||
Ok(match applepay_response {
|
||||
Ok(_) => services::api::ApplicationResponse::Json(ApplepayMerchantResponse {
|
||||
status_code: "200".to_string(),
|
||||
status_message: "Applepay verification Completed".to_string(),
|
||||
}),
|
||||
Err(error) => {
|
||||
logger::error!(?error);
|
||||
services::api::ApplicationResponse::Json(ApplepayMerchantResponse {
|
||||
status_code: "200".to_string(),
|
||||
status_message: "Applepay verification Failed".to_string(),
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn log_applepay_verification_response_if_error(
|
||||
response: &Result<Result<types::Response, types::Response>, Report<errors::ApiClientError>>,
|
||||
) {
|
||||
if let Err(error) = response.as_ref() {
|
||||
logger::error!(?error);
|
||||
};
|
||||
response
|
||||
.as_ref()
|
||||
.ok()
|
||||
.map(|res| res.as_ref().map_err(|error| logger::error!(?error)));
|
||||
}
|
||||
@ -201,6 +201,8 @@ pub enum Flow {
|
||||
BusinessProfileDelete,
|
||||
/// List all the business profiles for a merchant
|
||||
BusinessProfileList,
|
||||
/// Different verification flows
|
||||
Verification,
|
||||
}
|
||||
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user