feat(router): verify service for applepay merchant registration (#2009)

This commit is contained in:
Prajjwal Kumar
2023-08-31 13:20:06 +05:30
committed by GitHub
parent 098dc89d0c
commit 636b871b11
11 changed files with 214 additions and 0 deletions

View File

@ -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

View File

@ -16,4 +16,5 @@ pub mod payments;
#[cfg(feature = "payouts")]
pub mod payouts;
pub mod refunds;
pub mod verifications;
pub mod webhooks;

View 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,
}

View File

@ -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)]

View File

@ -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()));

View File

@ -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,

View File

@ -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)),
)
}
}

View 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
}

View File

@ -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;

View 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)));
}

View File

@ -201,6 +201,8 @@ pub enum Flow {
BusinessProfileDelete,
/// List all the business profiles for a merchant
BusinessProfileList,
/// Different verification flows
Verification,
}
///