mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 17:19:15 +08:00
feat(router): Move organization_id to request header from request body for v2 (#6277)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Co-authored-by: Narayan Bhat <48803246+Narayanbhat166@users.noreply.github.com>
This commit is contained in:
@ -453,6 +453,20 @@
|
||||
"summary": "Merchant Account - Create",
|
||||
"description": "Create a new account for a *merchant* and the *merchant* could be a seller or retailer or client who likes to receive and send payments.\n\nBefore creating the merchant account, it is mandatory to create an organization.",
|
||||
"operationId": "Create a Merchant Account",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "X-Organization-Id",
|
||||
"in": "header",
|
||||
"description": "Organization ID for which the merchant account has to be created.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"example": {
|
||||
"X-Organization-Id": "org_abcdefghijklmnop"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
@ -466,8 +480,7 @@
|
||||
"primary_contact_person": "John Doe",
|
||||
"primary_email": "example@company.com"
|
||||
},
|
||||
"merchant_name": "Cloth Store",
|
||||
"organization_id": "org_abcdefghijklmnop"
|
||||
"merchant_name": "Cloth Store"
|
||||
}
|
||||
},
|
||||
"Create a merchant account with metadata": {
|
||||
@ -476,14 +489,12 @@
|
||||
"metadata": {
|
||||
"key_1": "John Doe",
|
||||
"key_2": "Trends"
|
||||
},
|
||||
"organization_id": "org_abcdefghijklmnop"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Create a merchant account with minimal fields": {
|
||||
"value": {
|
||||
"merchant_name": "Cloth Store",
|
||||
"organization_id": "org_abcdefghijklmnop"
|
||||
"merchant_name": "Cloth Store"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9385,8 +9396,7 @@
|
||||
"MerchantAccountCreate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"merchant_name",
|
||||
"organization_id"
|
||||
"merchant_name"
|
||||
],
|
||||
"properties": {
|
||||
"merchant_name": {
|
||||
@ -9407,13 +9417,6 @@
|
||||
"type": "object",
|
||||
"description": "Metadata is useful for storing additional, unstructured information about the merchant account.",
|
||||
"nullable": true
|
||||
},
|
||||
"organization_id": {
|
||||
"type": "string",
|
||||
"description": "The id of the organization to which the merchant belongs to. Please use the organization endpoint to create an organization",
|
||||
"example": "org_q98uSGAYbjEwqs0mJwnz",
|
||||
"maxLength": 64,
|
||||
"minLength": 1
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
||||
@ -178,7 +178,8 @@ impl MerchantAccountCreate {
|
||||
#[cfg(feature = "v2")]
|
||||
#[derive(Clone, Debug, Deserialize, ToSchema, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct MerchantAccountCreate {
|
||||
#[schema(as = MerchantAccountCreate)]
|
||||
pub struct MerchantAccountCreateWithoutOrgId {
|
||||
/// Name of the Merchant Account, This will be used as a prefix to generate the id
|
||||
#[schema(value_type= String, max_length = 64, example = "NewAge Retailer")]
|
||||
pub merchant_name: Secret<common_utils::new_type::MerchantName>,
|
||||
@ -189,9 +190,17 @@ pub struct MerchantAccountCreate {
|
||||
/// Metadata is useful for storing additional, unstructured information about the merchant account.
|
||||
#[schema(value_type = Option<Object>, example = r#"{ "city": "NY", "unit": "245" }"#)]
|
||||
pub metadata: Option<pii::SecretSerdeValue>,
|
||||
}
|
||||
|
||||
/// The id of the organization to which the merchant belongs to. Please use the organization endpoint to create an organization
|
||||
#[schema(value_type = String, max_length = 64, min_length = 1, example = "org_q98uSGAYbjEwqs0mJwnz")]
|
||||
// In v2 the struct used in the API is MerchantAccountCreateWithoutOrgId
|
||||
// The following struct is only used internally, so we can reuse the common
|
||||
// part of `create_merchant_account` without duplicating its code for v2
|
||||
#[cfg(feature = "v2")]
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct MerchantAccountCreate {
|
||||
pub merchant_name: Secret<common_utils::new_type::MerchantName>,
|
||||
pub merchant_details: Option<MerchantDetails>,
|
||||
pub metadata: Option<pii::SecretSerdeValue>,
|
||||
pub organization_id: id_type::OrganizationId,
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
use crate::errors::{CustomResult, ValidationError};
|
||||
|
||||
crate::id_type!(
|
||||
OrganizationId,
|
||||
"A type for organization_id that can be used for organization ids"
|
||||
@ -13,3 +15,10 @@ crate::impl_generate_id_id_type!(OrganizationId, "org");
|
||||
crate::impl_serializable_secret_id_type!(OrganizationId);
|
||||
crate::impl_queryable_id_type!(OrganizationId);
|
||||
crate::impl_to_sql_from_sql_id_type!(OrganizationId);
|
||||
|
||||
impl OrganizationId {
|
||||
/// Get an organization id from String
|
||||
pub fn wrap(org_id: String) -> CustomResult<Self, ValidationError> {
|
||||
Self::try_from(std::borrow::Cow::from(org_id))
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,7 +154,7 @@ Never share your secret api keys. Keep them guarded and secure.
|
||||
api_models::organization::OrganizationCreateRequest,
|
||||
api_models::organization::OrganizationUpdateRequest,
|
||||
api_models::organization::OrganizationResponse,
|
||||
api_models::admin::MerchantAccountCreate,
|
||||
api_models::admin::MerchantAccountCreateWithoutOrgId,
|
||||
api_models::admin::MerchantAccountUpdate,
|
||||
api_models::admin::MerchantAccountDeleteResponse,
|
||||
api_models::admin::MerchantConnectorDeleteResponse,
|
||||
|
||||
@ -51,6 +51,13 @@ pub async fn merchant_account_create() {}
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/v2/merchant_accounts",
|
||||
params(
|
||||
(
|
||||
"X-Organization-Id" = String, Header,
|
||||
description = "Organization ID for which the merchant account has to be created.",
|
||||
example = json!({"X-Organization-Id": "org_abcdefghijklmnop"})
|
||||
),
|
||||
),
|
||||
request_body(
|
||||
content = MerchantAccountCreate,
|
||||
examples(
|
||||
@ -58,7 +65,6 @@ pub async fn merchant_account_create() {}
|
||||
"Create a merchant account with minimal fields" = (
|
||||
value = json!({
|
||||
"merchant_name": "Cloth Store",
|
||||
"organization_id": "org_abcdefghijklmnop"
|
||||
})
|
||||
)
|
||||
),
|
||||
@ -66,7 +72,6 @@ pub async fn merchant_account_create() {}
|
||||
"Create a merchant account with merchant details" = (
|
||||
value = json!({
|
||||
"merchant_name": "Cloth Store",
|
||||
"organization_id": "org_abcdefghijklmnop",
|
||||
"merchant_details": {
|
||||
"primary_contact_person": "John Doe",
|
||||
"primary_email": "example@company.com"
|
||||
@ -78,7 +83,6 @@ pub async fn merchant_account_create() {}
|
||||
"Create a merchant account with metadata" = (
|
||||
value = json!({
|
||||
"merchant_name": "Cloth Store",
|
||||
"organization_id": "org_abcdefghijklmnop",
|
||||
"metadata": {
|
||||
"key_1": "John Doe",
|
||||
"key_2": "Trends"
|
||||
|
||||
@ -66,6 +66,7 @@ pub mod headers {
|
||||
pub const X_API_VERSION: &str = "X-ApiVersion";
|
||||
pub const X_FORWARDED_FOR: &str = "X-Forwarded-For";
|
||||
pub const X_MERCHANT_ID: &str = "X-Merchant-Id";
|
||||
pub const X_ORGANIZATION_ID: &str = "X-Organization-Id";
|
||||
pub const X_LOGIN: &str = "X-Login";
|
||||
pub const X_TRANS_KEY: &str = "X-Trans-Key";
|
||||
pub const X_VERSION: &str = "X-Version";
|
||||
|
||||
@ -92,7 +92,7 @@ pub async fn organization_retrieve(
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(feature = "olap")]
|
||||
#[cfg(all(feature = "olap", feature = "v1"))]
|
||||
#[instrument(skip_all, fields(flow = ?Flow::MerchantsAccountCreate))]
|
||||
pub async fn merchant_account_create(
|
||||
state: web::Data<AppState>,
|
||||
@ -112,6 +112,43 @@ pub async fn merchant_account_create(
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "olap", feature = "v2"))]
|
||||
#[instrument(skip_all, fields(flow = ?Flow::MerchantsAccountCreate))]
|
||||
pub async fn merchant_account_create(
|
||||
state: web::Data<AppState>,
|
||||
req: HttpRequest,
|
||||
json_payload: web::Json<api_models::admin::MerchantAccountCreateWithoutOrgId>,
|
||||
) -> HttpResponse {
|
||||
let flow = Flow::MerchantsAccountCreate;
|
||||
let headers = req.headers();
|
||||
|
||||
let org_id = match auth::HeaderMapStruct::new(headers).get_organization_id_from_header() {
|
||||
Ok(org_id) => org_id,
|
||||
Err(e) => return api::log_and_return_error_response(e),
|
||||
};
|
||||
|
||||
// Converting from MerchantAccountCreateWithoutOrgId to MerchantAccountCreate so we can use the existing
|
||||
// `create_merchant_account` function for v2 as well
|
||||
let json_payload = json_payload.into_inner();
|
||||
let new_request_payload_with_org_id = api_models::admin::MerchantAccountCreate {
|
||||
merchant_name: json_payload.merchant_name,
|
||||
merchant_details: json_payload.merchant_details,
|
||||
metadata: json_payload.metadata,
|
||||
organization_id: org_id,
|
||||
};
|
||||
|
||||
Box::pin(api::server_wrap(
|
||||
flow,
|
||||
state,
|
||||
&req,
|
||||
new_request_payload_with_org_id,
|
||||
|state, _, req, _| create_merchant_account(state, req),
|
||||
&auth::AdminApiAuth,
|
||||
api_locking::LockAction::NotApplicable,
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
/// Merchant Account - Retrieve
|
||||
///
|
||||
/// Retrieve a merchant account details.
|
||||
|
||||
@ -931,7 +931,7 @@ where
|
||||
}
|
||||
|
||||
/// A helper struct to extract headers from the request
|
||||
struct HeaderMapStruct<'a> {
|
||||
pub(crate) struct HeaderMapStruct<'a> {
|
||||
headers: &'a HeaderMap,
|
||||
}
|
||||
|
||||
@ -981,6 +981,18 @@ impl<'a> HeaderMapStruct<'a> {
|
||||
)
|
||||
})
|
||||
}
|
||||
#[cfg(feature = "v2")]
|
||||
pub fn get_organization_id_from_header(&self) -> RouterResult<id_type::OrganizationId> {
|
||||
self.get_mandatory_header_value_by_key(headers::X_ORGANIZATION_ID)
|
||||
.map(|val| val.to_owned())
|
||||
.and_then(|organization_id| {
|
||||
id_type::OrganizationId::wrap(organization_id).change_context(
|
||||
errors::ApiErrorResponse::InvalidRequestData {
|
||||
message: format!("`{}` header is invalid", headers::X_ORGANIZATION_ID),
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the merchant-id from `x-merchant-id` header
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
{
|
||||
"ma_create": {
|
||||
"merchant_name": "Hyperswitch Seller",
|
||||
"organization_id": ""
|
||||
"merchant_name": "Hyperswitch Seller"
|
||||
},
|
||||
"ma_update": {
|
||||
"merchant_name": "Hyperswitch"
|
||||
|
||||
@ -173,15 +173,13 @@ Cypress.Commands.add(
|
||||
.replaceAll(" ", "")
|
||||
.toLowerCase();
|
||||
|
||||
// Update request body
|
||||
merchantAccountCreateBody.organization_id = organization_id;
|
||||
|
||||
cy.request({
|
||||
method: "POST",
|
||||
url: url,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"api-key": api_key,
|
||||
"X-Organization-Id": organization_id,
|
||||
},
|
||||
body: merchantAccountCreateBody,
|
||||
failOnStatusCode: false,
|
||||
|
||||
Reference in New Issue
Block a user