mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 00:49:42 +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",
|
"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.",
|
"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",
|
"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": {
|
"requestBody": {
|
||||||
"content": {
|
"content": {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
@ -466,8 +480,7 @@
|
|||||||
"primary_contact_person": "John Doe",
|
"primary_contact_person": "John Doe",
|
||||||
"primary_email": "example@company.com"
|
"primary_email": "example@company.com"
|
||||||
},
|
},
|
||||||
"merchant_name": "Cloth Store",
|
"merchant_name": "Cloth Store"
|
||||||
"organization_id": "org_abcdefghijklmnop"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Create a merchant account with metadata": {
|
"Create a merchant account with metadata": {
|
||||||
@ -476,14 +489,12 @@
|
|||||||
"metadata": {
|
"metadata": {
|
||||||
"key_1": "John Doe",
|
"key_1": "John Doe",
|
||||||
"key_2": "Trends"
|
"key_2": "Trends"
|
||||||
},
|
}
|
||||||
"organization_id": "org_abcdefghijklmnop"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Create a merchant account with minimal fields": {
|
"Create a merchant account with minimal fields": {
|
||||||
"value": {
|
"value": {
|
||||||
"merchant_name": "Cloth Store",
|
"merchant_name": "Cloth Store"
|
||||||
"organization_id": "org_abcdefghijklmnop"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -9385,8 +9396,7 @@
|
|||||||
"MerchantAccountCreate": {
|
"MerchantAccountCreate": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
"merchant_name",
|
"merchant_name"
|
||||||
"organization_id"
|
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"merchant_name": {
|
"merchant_name": {
|
||||||
@ -9407,13 +9417,6 @@
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "Metadata is useful for storing additional, unstructured information about the merchant account.",
|
"description": "Metadata is useful for storing additional, unstructured information about the merchant account.",
|
||||||
"nullable": true
|
"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
|
"additionalProperties": false
|
||||||
|
|||||||
@ -178,7 +178,8 @@ impl MerchantAccountCreate {
|
|||||||
#[cfg(feature = "v2")]
|
#[cfg(feature = "v2")]
|
||||||
#[derive(Clone, Debug, Deserialize, ToSchema, Serialize)]
|
#[derive(Clone, Debug, Deserialize, ToSchema, Serialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[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
|
/// 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")]
|
#[schema(value_type= String, max_length = 64, example = "NewAge Retailer")]
|
||||||
pub merchant_name: Secret<common_utils::new_type::MerchantName>,
|
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.
|
/// Metadata is useful for storing additional, unstructured information about the merchant account.
|
||||||
#[schema(value_type = Option<Object>, example = r#"{ "city": "NY", "unit": "245" }"#)]
|
#[schema(value_type = Option<Object>, example = r#"{ "city": "NY", "unit": "245" }"#)]
|
||||||
pub metadata: Option<pii::SecretSerdeValue>,
|
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
|
// In v2 the struct used in the API is MerchantAccountCreateWithoutOrgId
|
||||||
#[schema(value_type = String, max_length = 64, min_length = 1, example = "org_q98uSGAYbjEwqs0mJwnz")]
|
// 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,
|
pub organization_id: id_type::OrganizationId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
use crate::errors::{CustomResult, ValidationError};
|
||||||
|
|
||||||
crate::id_type!(
|
crate::id_type!(
|
||||||
OrganizationId,
|
OrganizationId,
|
||||||
"A type for organization_id that can be used for organization ids"
|
"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_serializable_secret_id_type!(OrganizationId);
|
||||||
crate::impl_queryable_id_type!(OrganizationId);
|
crate::impl_queryable_id_type!(OrganizationId);
|
||||||
crate::impl_to_sql_from_sql_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::OrganizationCreateRequest,
|
||||||
api_models::organization::OrganizationUpdateRequest,
|
api_models::organization::OrganizationUpdateRequest,
|
||||||
api_models::organization::OrganizationResponse,
|
api_models::organization::OrganizationResponse,
|
||||||
api_models::admin::MerchantAccountCreate,
|
api_models::admin::MerchantAccountCreateWithoutOrgId,
|
||||||
api_models::admin::MerchantAccountUpdate,
|
api_models::admin::MerchantAccountUpdate,
|
||||||
api_models::admin::MerchantAccountDeleteResponse,
|
api_models::admin::MerchantAccountDeleteResponse,
|
||||||
api_models::admin::MerchantConnectorDeleteResponse,
|
api_models::admin::MerchantConnectorDeleteResponse,
|
||||||
|
|||||||
@ -51,6 +51,13 @@ pub async fn merchant_account_create() {}
|
|||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
post,
|
post,
|
||||||
path = "/v2/merchant_accounts",
|
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(
|
request_body(
|
||||||
content = MerchantAccountCreate,
|
content = MerchantAccountCreate,
|
||||||
examples(
|
examples(
|
||||||
@ -58,7 +65,6 @@ pub async fn merchant_account_create() {}
|
|||||||
"Create a merchant account with minimal fields" = (
|
"Create a merchant account with minimal fields" = (
|
||||||
value = json!({
|
value = json!({
|
||||||
"merchant_name": "Cloth Store",
|
"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" = (
|
"Create a merchant account with merchant details" = (
|
||||||
value = json!({
|
value = json!({
|
||||||
"merchant_name": "Cloth Store",
|
"merchant_name": "Cloth Store",
|
||||||
"organization_id": "org_abcdefghijklmnop",
|
|
||||||
"merchant_details": {
|
"merchant_details": {
|
||||||
"primary_contact_person": "John Doe",
|
"primary_contact_person": "John Doe",
|
||||||
"primary_email": "example@company.com"
|
"primary_email": "example@company.com"
|
||||||
@ -78,7 +83,6 @@ pub async fn merchant_account_create() {}
|
|||||||
"Create a merchant account with metadata" = (
|
"Create a merchant account with metadata" = (
|
||||||
value = json!({
|
value = json!({
|
||||||
"merchant_name": "Cloth Store",
|
"merchant_name": "Cloth Store",
|
||||||
"organization_id": "org_abcdefghijklmnop",
|
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"key_1": "John Doe",
|
"key_1": "John Doe",
|
||||||
"key_2": "Trends"
|
"key_2": "Trends"
|
||||||
|
|||||||
@ -66,6 +66,7 @@ pub mod headers {
|
|||||||
pub const X_API_VERSION: &str = "X-ApiVersion";
|
pub const X_API_VERSION: &str = "X-ApiVersion";
|
||||||
pub const X_FORWARDED_FOR: &str = "X-Forwarded-For";
|
pub const X_FORWARDED_FOR: &str = "X-Forwarded-For";
|
||||||
pub const X_MERCHANT_ID: &str = "X-Merchant-Id";
|
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_LOGIN: &str = "X-Login";
|
||||||
pub const X_TRANS_KEY: &str = "X-Trans-Key";
|
pub const X_TRANS_KEY: &str = "X-Trans-Key";
|
||||||
pub const X_VERSION: &str = "X-Version";
|
pub const X_VERSION: &str = "X-Version";
|
||||||
|
|||||||
@ -92,7 +92,7 @@ pub async fn organization_retrieve(
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "olap")]
|
#[cfg(all(feature = "olap", feature = "v1"))]
|
||||||
#[instrument(skip_all, fields(flow = ?Flow::MerchantsAccountCreate))]
|
#[instrument(skip_all, fields(flow = ?Flow::MerchantsAccountCreate))]
|
||||||
pub async fn merchant_account_create(
|
pub async fn merchant_account_create(
|
||||||
state: web::Data<AppState>,
|
state: web::Data<AppState>,
|
||||||
@ -112,6 +112,43 @@ pub async fn merchant_account_create(
|
|||||||
.await
|
.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
|
/// Merchant Account - Retrieve
|
||||||
///
|
///
|
||||||
/// Retrieve a merchant account details.
|
/// Retrieve a merchant account details.
|
||||||
|
|||||||
@ -931,7 +931,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A helper struct to extract headers from the request
|
/// A helper struct to extract headers from the request
|
||||||
struct HeaderMapStruct<'a> {
|
pub(crate) struct HeaderMapStruct<'a> {
|
||||||
headers: &'a HeaderMap,
|
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
|
/// Get the merchant-id from `x-merchant-id` header
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"ma_create": {
|
"ma_create": {
|
||||||
"merchant_name": "Hyperswitch Seller",
|
"merchant_name": "Hyperswitch Seller"
|
||||||
"organization_id": ""
|
|
||||||
},
|
},
|
||||||
"ma_update": {
|
"ma_update": {
|
||||||
"merchant_name": "Hyperswitch"
|
"merchant_name": "Hyperswitch"
|
||||||
|
|||||||
@ -173,15 +173,13 @@ Cypress.Commands.add(
|
|||||||
.replaceAll(" ", "")
|
.replaceAll(" ", "")
|
||||||
.toLowerCase();
|
.toLowerCase();
|
||||||
|
|
||||||
// Update request body
|
|
||||||
merchantAccountCreateBody.organization_id = organization_id;
|
|
||||||
|
|
||||||
cy.request({
|
cy.request({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: url,
|
url: url,
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"api-key": api_key,
|
"api-key": api_key,
|
||||||
|
"X-Organization-Id": organization_id,
|
||||||
},
|
},
|
||||||
body: merchantAccountCreateBody,
|
body: merchantAccountCreateBody,
|
||||||
failOnStatusCode: false,
|
failOnStatusCode: false,
|
||||||
|
|||||||
Reference in New Issue
Block a user