feat(openapi): automatically generate OpenAPI spec from code (#318)

Co-authored-by: bernard eugine <bernard.eugine@bernard.eugine-MacBookPro>
Co-authored-by: Sanchith Hegde <sanchith.hegde@juspay.in>
This commit is contained in:
bernard-eugine
2023-01-09 16:09:49 +05:30
committed by GitHub
parent c807713a6f
commit 3fe60b5a48
15 changed files with 1401 additions and 14 deletions

218
Cargo.lock generated
View File

@ -292,6 +292,15 @@ dependencies = [
"alloc-no-stdlib", "alloc-no-stdlib",
] ]
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "ansi_term" name = "ansi_term"
version = "0.12.1" version = "0.12.1"
@ -320,6 +329,7 @@ dependencies = [
"serde_json", "serde_json",
"strum", "strum",
"time", "time",
"utoipa",
] ]
[[package]] [[package]]
@ -918,6 +928,19 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f"
dependencies = [
"iana-time-zone",
"num-integer",
"num-traits",
"serde",
"winapi",
]
[[package]] [[package]]
name = "clap" name = "clap"
version = "2.34.0" version = "2.34.0"
@ -927,12 +950,22 @@ dependencies = [
"ansi_term", "ansi_term",
"atty", "atty",
"bitflags", "bitflags",
"strsim", "strsim 0.8.0",
"textwrap", "textwrap",
"unicode-width", "unicode-width",
"vec_map", "vec_map",
] ]
[[package]]
name = "codespan-reporting"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
dependencies = [
"termcolor",
"unicode-width",
]
[[package]] [[package]]
name = "common_utils" name = "common_utils"
version = "0.1.0" version = "0.1.0"
@ -1086,6 +1119,85 @@ dependencies = [
"sct 0.6.1", "sct 0.6.1",
] ]
[[package]]
name = "cxx"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5add3fc1717409d029b20c5b6903fc0c0b02fa6741d820054f4a2efa5e5816fd"
dependencies = [
"cc",
"cxxbridge-flags",
"cxxbridge-macro",
"link-cplusplus",
]
[[package]]
name = "cxx-build"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4c87959ba14bc6fbc61df77c3fcfe180fc32b93538c4f1031dd802ccb5f2ff0"
dependencies = [
"cc",
"codespan-reporting",
"once_cell",
"proc-macro2",
"quote",
"scratch",
"syn",
]
[[package]]
name = "cxxbridge-flags"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69a3e162fde4e594ed2b07d0f83c6c67b745e7f28ce58c6df5e6b6bef99dfb59"
[[package]]
name = "cxxbridge-macro"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e7e2adeb6a0d4a282e581096b06e1791532b7d576dcde5ccd9382acf55db8e6"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "darling"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim 0.10.0",
"syn",
]
[[package]]
name = "darling_macro"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e"
dependencies = [
"darling_core",
"quote",
"syn",
]
[[package]] [[package]]
name = "dashmap" name = "dashmap"
version = "5.4.0" version = "5.4.0"
@ -1820,6 +1932,36 @@ dependencies = [
"tokio-native-tls", "tokio-native-tls",
] ]
[[package]]
name = "iana-time-zone"
version = "0.1.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"winapi",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
dependencies = [
"cxx",
"cxx-build",
]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]] [[package]]
name = "idna" name = "idna"
version = "0.3.0" version = "0.3.0"
@ -1838,6 +1980,7 @@ checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"hashbrown", "hashbrown",
"serde",
] ]
[[package]] [[package]]
@ -1987,6 +2130,15 @@ dependencies = [
"vcpkg", "vcpkg",
] ]
[[package]]
name = "link-cplusplus"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5"
dependencies = [
"cc",
]
[[package]] [[package]]
name = "linked-hash-map" name = "linked-hash-map"
version = "0.5.6" version = "0.5.6"
@ -2950,6 +3102,7 @@ dependencies = [
"tokio", "tokio",
"toml", "toml",
"url", "url",
"utoipa",
"uuid", "uuid",
"wiremock", "wiremock",
] ]
@ -3103,6 +3256,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "scratch"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2"
[[package]] [[package]]
name = "sct" name = "sct"
version = "0.6.1" version = "0.6.1"
@ -3227,6 +3386,34 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "serde_with"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25bf4a5a814902cd1014dbccfa4d4560fb8432c779471e96e035602519f82eef"
dependencies = [
"base64 0.13.1",
"chrono",
"hex",
"indexmap",
"serde",
"serde_json",
"serde_with_macros",
"time",
]
[[package]]
name = "serde_with_macros"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3452b4c0f6c1e357f73fdb87cd1efabaa12acf328c7a528e252893baeb3f4aa"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "serial_test" name = "serial_test"
version = "0.10.0" version = "0.10.0"
@ -3353,6 +3540,12 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]] [[package]]
name = "structopt" name = "structopt"
version = "0.3.26" version = "0.3.26"
@ -3953,6 +4146,29 @@ version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8db7427f936968176eaa7cdf81b7f98b980b18495ec28f1b5791ac3bfe3eea9" checksum = "e8db7427f936968176eaa7cdf81b7f98b980b18495ec28f1b5791ac3bfe3eea9"
[[package]]
name = "utoipa"
version = "2.4.2"
source = "git+https://github.com/SanchithHegde/utoipa?branch=retain-property-order#4ca5604f94484c7a5a7852972c9b6e68f33a871e"
dependencies = [
"indexmap",
"serde",
"serde_json",
"serde_with",
"utoipa-gen",
]
[[package]]
name = "utoipa-gen"
version = "2.4.2"
source = "git+https://github.com/SanchithHegde/utoipa?branch=retain-property-order#4ca5604f94484c7a5a7852972c9b6e68f33a871e"
dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.2.2" version = "1.2.2"

View File

@ -12,6 +12,7 @@ serde = { version = "1.0.145", features = ["derive"] }
serde_json = "1.0.85" serde_json = "1.0.85"
strum = { version = "0.24.1", features = ["derive"] } strum = { version = "0.24.1", features = ["derive"] }
time = { version = "0.3.14", features = ["serde", "serde-well-known", "std"] } time = { version = "0.3.14", features = ["serde", "serde-well-known", "std"] }
utoipa = { git = "https://github.com/SanchithHegde/utoipa", branch = "retain-property-order" }
# First party crates # First party crates
common_utils = { version = "0.1.0", path = "../common_utils" } common_utils = { version = "0.1.0", path = "../common_utils" }

View File

@ -1,75 +1,207 @@
use common_utils::pii; use common_utils::pii;
use masking::{Secret, StrongSecret}; use masking::{Secret, StrongSecret};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
pub use self::CreateMerchantAccount as MerchantAccountResponse; pub use self::CreateMerchantAccount as MerchantAccountResponse;
use super::payments::AddressDetails; use super::payments::AddressDetails;
use crate::{enums as api_enums, payment_methods}; use crate::{enums as api_enums, payment_methods};
#[derive(Clone, Debug, Deserialize, ToSchema, Serialize)]
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct CreateMerchantAccount { pub struct CreateMerchantAccount {
/// The identifier for the Merchant Account
#[schema(max_length = 255, example = "y3oqhf46pyzuxjbcn2giaqnb44")]
pub merchant_id: String, pub merchant_id: String,
/// Name of the Merchant Account
#[schema(example = "NewAge Retailer")]
pub merchant_name: Option<String>, pub merchant_name: Option<String>,
/// API key that will be used for server side API access
#[schema(value_type = Option<String>, example = "Ah2354543543523")]
pub api_key: Option<StrongSecret<String>>, pub api_key: Option<StrongSecret<String>>,
/// Merchant related details
pub merchant_details: Option<MerchantDetails>, pub merchant_details: Option<MerchantDetails>,
/// The URL to redirect after the completion of the operation
#[schema(max_length = 255, example = "www.example.com/success")]
pub return_url: Option<String>, pub return_url: Option<String>,
/// Webhook related details
pub webhook_details: Option<WebhookDetails>, pub webhook_details: Option<WebhookDetails>,
/// The routing algorithm to be used to process the incoming request from merchant to outgoing payment processor or payment method. The default is 'Custom'
#[schema(value_type = Option<RoutingAlgorithm>, max_length = 255, example = "custom")]
pub routing_algorithm: Option<api_enums::RoutingAlgorithm>, pub routing_algorithm: Option<api_enums::RoutingAlgorithm>,
/// The custom routing rules to be used for various payment methods and conditions
pub custom_routing_rules: Option<Vec<CustomRoutingRules>>, pub custom_routing_rules: Option<Vec<CustomRoutingRules>>,
/// A boolean value to indicate if the merchant is a sub-merchant under a master or a parent merchant. By default, its value is false.
#[schema(default = false, example = false)]
pub sub_merchants_enabled: Option<bool>, pub sub_merchants_enabled: Option<bool>,
/// Refers to the Parent Merchant ID if the merchant being created is a sub-merchant
#[schema(max_length = 255, example = "xkkdf909012sdjki2dkh5sdf")]
pub parent_merchant_id: Option<String>, pub parent_merchant_id: Option<String>,
/// A boolean value to indicate if payment response hash needs to be enabled
#[schema(default = false, example = true)]
pub enable_payment_response_hash: Option<bool>, pub enable_payment_response_hash: Option<bool>,
/// Refers to the hash key used for payment response
pub payment_response_hash_key: Option<String>, pub payment_response_hash_key: Option<String>,
/// A boolean value to indicate if redirect to merchant with http post needs to be enabled
#[schema(default = false, example = true)]
pub redirect_to_merchant_with_http_post: Option<bool>, pub redirect_to_merchant_with_http_post: Option<bool>,
/// You can specify up to 50 keys, with key names up to 40 characters long and values up to 500 characters long. Metadata is useful for storing additional, structured information on an object.
#[schema(value_type = Option<Object>, example = r#"{ "city": "NY", "unit": "245" }"#)]
pub metadata: Option<serde_json::Value>, pub metadata: Option<serde_json::Value>,
/// API key that will be used for server side API access
#[schema(example = "AH3423bkjbkjdsfbkj")]
pub publishable_key: Option<String>, pub publishable_key: Option<String>,
pub locker_id: Option<String>, pub locker_id: Option<String>,
} }
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, ToSchema, Serialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct MerchantDetails { pub struct MerchantDetails {
/// The merchant's primary contact name
#[schema(value_type = Option<String>, max_length = 255, example = "John Doe")]
pub primary_contact_person: Option<Secret<String>>, pub primary_contact_person: Option<Secret<String>>,
/// The merchant's primary phone number
#[schema(value_type = Option<String>, max_length = 255, example = "999999999")]
pub primary_phone: Option<Secret<String>>, pub primary_phone: Option<Secret<String>>,
/// The merchant's primary email address
#[schema(value_type = Option<String>, max_length = 255, example = "johndoe@test.com")]
pub primary_email: Option<Secret<String, pii::Email>>, pub primary_email: Option<Secret<String, pii::Email>>,
/// The merchant's secondary contact name
#[schema(value_type = Option<String>, max_length= 255, example = "John Doe2")]
pub secondary_contact_person: Option<Secret<String>>, pub secondary_contact_person: Option<Secret<String>>,
/// The merchant's secondary phone number
#[schema(value_type = Option<String>, max_length = 255, example = "999999988")]
pub secondary_phone: Option<Secret<String>>, pub secondary_phone: Option<Secret<String>>,
/// The merchant's secondary email address
#[schema(value_type = Option<String>, max_length = 255, example = "johndoe2@test.com")]
pub secondary_email: Option<Secret<String, pii::Email>>, pub secondary_email: Option<Secret<String, pii::Email>>,
/// The business website of the merchant
#[schema(max_length = 255, example = "www.example.com")]
pub website: Option<String>, pub website: Option<String>,
/// A brief description about merchant's business
#[schema(
max_length = 255,
example = "Online Retail with a wide selection of organic products for North America"
)]
pub about_business: Option<String>, pub about_business: Option<String>,
/// The merchant's address details
pub address: Option<AddressDetails>, pub address: Option<AddressDetails>,
} }
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, ToSchema, Serialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct WebhookDetails { pub struct WebhookDetails {
///The version for Webhook
#[schema(max_length = 255, max_length = 255, example = "1.0.2")]
pub webhook_version: Option<String>, pub webhook_version: Option<String>,
///The user name for Webhook login
#[schema(max_length = 255, max_length = 255, example = "ekart_retail")]
pub webhook_username: Option<String>, pub webhook_username: Option<String>,
///The password for Webhook login
#[schema(value_type = Option<String>, max_length = 255, example = "ekart@123")]
pub webhook_password: Option<Secret<String>>, pub webhook_password: Option<Secret<String>>,
///The url for the webhook endpoint
#[schema(value_type = Option<String>, example = "www.ekart.com/webhooks")]
pub webhook_url: Option<Secret<String>>, pub webhook_url: Option<Secret<String>>,
/// If this property is true, a webhook message is posted whenever a new payment is created
#[schema(example = true)]
pub payment_created_enabled: Option<bool>, pub payment_created_enabled: Option<bool>,
/// If this property is true, a webhook message is posted whenever a payment is successful
#[schema(example = true)]
pub payment_succeeded_enabled: Option<bool>, pub payment_succeeded_enabled: Option<bool>,
/// If this property is true, a webhook message is posted whenever a payment fails
#[schema(example = true)]
pub payment_failed_enabled: Option<bool>, pub payment_failed_enabled: Option<bool>,
} }
#[derive(Default, Clone, Debug, Deserialize, Serialize)] #[derive(Default, Clone, Debug, Deserialize, ToSchema, Serialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct CustomRoutingRules { pub struct CustomRoutingRules {
/// The List of payment methods to include for this routing rule
#[schema(value_type = Option<Vec<PaymentMethodType>>, example = json!(["card", "upi"]))]
pub payment_methods_incl: Option<Vec<api_enums::PaymentMethodType>>, pub payment_methods_incl: Option<Vec<api_enums::PaymentMethodType>>,
/// The List of payment methods to exclude for this routing rule. If there is conflict between include and exclude lists, include list overrides the exclude list.
#[schema(value_type = Option<Vec<PaymentMethodType>>, example = json!(["card", "upi"]))]
pub payment_methods_excl: Option<Vec<api_enums::PaymentMethodType>>, pub payment_methods_excl: Option<Vec<api_enums::PaymentMethodType>>,
/// The List of payment method types to include for this routing rule
#[schema(value_type = Option<Vec<PaymentMethodSubType>>, example = json!(["credit_card", "debit_card"]))]
pub payment_method_types_incl: Option<Vec<api_enums::PaymentMethodSubType>>, pub payment_method_types_incl: Option<Vec<api_enums::PaymentMethodSubType>>,
/// The List of payment method types to exclude for this routing rule. If there is conflict between include and exclude lists, include list overrides the exclude list.
#[schema(value_type = Option<Vec<PaymentMethodSubType>>, example = json!(["credit_card", "debit_card"]))]
pub payment_method_types_excl: Option<Vec<api_enums::PaymentMethodSubType>>, pub payment_method_types_excl: Option<Vec<api_enums::PaymentMethodSubType>>,
/// The List of payment method issuers to include for this routing rule
#[schema(example = json!(["Citibank", "JPMorgan"]))]
pub payment_method_issuers_incl: Option<Vec<String>>, pub payment_method_issuers_incl: Option<Vec<String>>,
/// The List of payment method issuers to exclude for this routing rule. If there is conflict between include and exclude lists, include list overrides the exclude list.
#[schema(example = json!(["Citibank", "JPMorgan"]))]
pub payment_method_issuers_excl: Option<Vec<String>>, pub payment_method_issuers_excl: Option<Vec<String>>,
/// The List of countries to include for this routing rule
#[schema(example = json!(["US", "UK"]))]
pub countries_incl: Option<Vec<String>>, pub countries_incl: Option<Vec<String>>,
/// The List of countries to exclude for this routing rule. If there is conflict between include and exclude lists, include list overrides the exclude list.
#[schema(example = json!(["US", "UK"]))]
pub countries_excl: Option<Vec<String>>, pub countries_excl: Option<Vec<String>>,
/// The List of currencies to include for this routing rule
#[schema(value_type = Option<Vec<Currency>>, example = json!(["EUR","USD"]))]
pub currencies_incl: Option<Vec<api_enums::Currency>>, pub currencies_incl: Option<Vec<api_enums::Currency>>,
/// The List of currencies to exclude for this routing rule. If there is conflict between include and exclude lists, include list overrides the exclude list.
#[schema(value_type = Option<Vec<Currency>>, example = json!(["EUR","USD"]))]
pub currencies_excl: Option<Vec<api_enums::Currency>>, pub currencies_excl: Option<Vec<api_enums::Currency>>,
/// List of Metadata Filter keys to apply for the Routing Rule. The filters are presented as 2 arrays of keys and value. This property contains all the keys.
#[schema(example = json!(["platform","Category"]))]
pub metadata_filters_keys: Option<Vec<String>>, pub metadata_filters_keys: Option<Vec<String>>,
/// List of Metadata Filters to apply for the Routing Rule. The filters are presented as 2 arrays of keys and value. This property contains all the values.
#[schema(example = json!(["android", "Category_Electronics"]))]
pub metadata_filters_values: Option<Vec<String>>, pub metadata_filters_values: Option<Vec<String>>,
/// The pecking order of payment connectors (or processors) to be used for routing. The first connector in the array will be attempted for routing. If it fails, the second connector will be used till the list is exhausted.
#[schema(example = json!([ "stripe", "adyen", "brain_tree"]))]
pub connectors_pecking_order: Option<Vec<String>>, pub connectors_pecking_order: Option<Vec<String>>,
///An Array of Connectors (as Keys) with the associated percentage of traffic to be routed through the given connector (Expressed as an array of values)
#[schema(example = json!([ "stripe", "adyen", "brain_tree"]))]
pub connectors_traffic_weightage_key: Option<Vec<String>>, pub connectors_traffic_weightage_key: Option<Vec<String>>,
/// An Array of Weightage (expressed in percentage) that needs to be associated with the respective connectors (Expressed as an array of keys)
#[schema(example = json!([ 50, 30, 20 ]))]
pub connectors_traffic_weightage_value: Option<Vec<i32>>, pub connectors_traffic_weightage_value: Option<Vec<i32>>,
} }
@ -84,7 +216,7 @@ pub struct MerchantId {
pub merchant_id: String, pub merchant_id: String,
} }
#[derive(Default, Debug, Deserialize, Serialize)] #[derive(Default, Debug, Deserialize, ToSchema, Serialize)]
pub struct MerchantConnectorId { pub struct MerchantConnectorId {
pub merchant_id: String, pub merchant_id: String,
pub merchant_connector_id: i32, pub merchant_connector_id: i32,

View File

@ -1,3 +1,5 @@
use utoipa::ToSchema;
#[derive( #[derive(
Clone, Clone,
Copy, Copy,
@ -125,6 +127,7 @@ pub enum ConnectorType {
serde::Serialize, serde::Serialize,
strum::Display, strum::Display,
strum::EnumString, strum::EnumString,
ToSchema,
frunk::LabelledGeneric, frunk::LabelledGeneric,
)] )]
pub enum Currency { pub enum Currency {
@ -339,6 +342,7 @@ pub enum PaymentMethodIssuerCode {
serde::Serialize, serde::Serialize,
strum::Display, strum::Display,
strum::EnumString, strum::EnumString,
ToSchema,
frunk::LabelledGeneric, frunk::LabelledGeneric,
)] )]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
@ -364,6 +368,7 @@ pub enum PaymentMethodSubType {
serde::Serialize, serde::Serialize,
strum::Display, strum::Display,
strum::EnumString, strum::EnumString,
ToSchema,
frunk::LabelledGeneric, frunk::LabelledGeneric,
)] )]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
@ -435,8 +440,12 @@ pub enum RefundStatus {
serde::Serialize, serde::Serialize,
strum::Display, strum::Display,
strum::EnumString, strum::EnumString,
ToSchema,
frunk::LabelledGeneric, frunk::LabelledGeneric,
)] )]
/// The routing algorithm to be used to process the incoming request from merchant to outgoing payment processor or payment method. The default is 'Custom'
#[schema(example = "custom")]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")] #[strum(serialize_all = "snake_case")]
pub enum RoutingAlgorithm { pub enum RoutingAlgorithm {

View File

@ -4,6 +4,7 @@ use common_utils::pii;
use masking::{PeekInterface, Secret}; use masking::{PeekInterface, Secret};
use router_derive::Setter; use router_derive::Setter;
use time::PrimitiveDateTime; use time::PrimitiveDateTime;
use utoipa::ToSchema;
use crate::{enums as api_enums, refunds}; use crate::{enums as api_enums, refunds};
@ -331,18 +332,45 @@ pub struct Address {
serde::Deserialize, serde::Deserialize,
serde::Serialize, serde::Serialize,
PartialEq, PartialEq,
ToSchema,
frunk::LabelledGeneric, frunk::LabelledGeneric,
)] )]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct AddressDetails { pub struct AddressDetails {
/// The address city
#[schema(max_length = 50, example = "New York")]
pub city: Option<String>, pub city: Option<String>,
/// The two-letter ISO country code for the address
#[schema(max_length = 2, min_length = 2, example = "US")]
pub country: Option<String>, pub country: Option<String>,
/// The first line of the address
#[schema(value_type = Option<String>, max_length = 200, example = "123, King Street")]
pub line1: Option<Secret<String>>, pub line1: Option<Secret<String>>,
/// The second line of the address
#[schema(value_type = Option<String>, max_length = 50, example = "Powelson Avenue")]
pub line2: Option<Secret<String>>, pub line2: Option<Secret<String>>,
/// The third line of the address
#[schema(value_type = Option<String>, max_length = 50, example = "Bridgewater")]
pub line3: Option<Secret<String>>, pub line3: Option<Secret<String>>,
/// The zip/postal code for the address
#[schema(value_type = Option<String>, max_length = 50, example = "08807")]
pub zip: Option<Secret<String>>, pub zip: Option<Secret<String>>,
/// The address state
#[schema(value_type = Option<String>, example = "New York")]
pub state: Option<Secret<String>>, pub state: Option<Secret<String>>,
/// The first name for the address
#[schema(value_type = Option<String>, max_length = 255, example = "John")]
pub first_name: Option<Secret<String>>, pub first_name: Option<Secret<String>>,
/// The last name for the address
#[schema(value_type = Option<String>, max_length = 255, example = "Doe")]
pub last_name: Option<Secret<String>>, pub last_name: Option<Secret<String>>,
} }

View File

@ -1,22 +1,51 @@
use common_utils::custom_serde; use common_utils::custom_serde;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use time::PrimitiveDateTime; use time::PrimitiveDateTime;
use utoipa::ToSchema;
use crate::enums; use crate::enums;
#[derive(Default, Debug, Clone, Deserialize)] #[derive(Default, Debug, ToSchema, Clone, Deserialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct RefundRequest { pub struct RefundRequest {
/// Unique Identifier for the Refund. This is to ensure idempotency for multiple partial refund initiated against the same payment. If the identifiers is not defined by the merchant, this filed shall be auto generated and provide in the API response. It is recommended to generate uuid(v4) as the refund_id.
#[schema(
max_length = 30,
min_length = 30,
example = "ref_mbabizu24mvu3mela5njyhpit4"
)]
pub refund_id: Option<String>, pub refund_id: Option<String>,
/// Total amount for which the refund is to be initiated. Amount for the payment in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc. If not provided, this will default to the full payment amount
#[schema(
max_length = 30,
min_length = 30,
example = "pay_mbabizu24mvu3mela5njyhpit4"
)]
pub payment_id: String, pub payment_id: String,
/// The identifier for the Merchant Account
#[schema(max_length = 255, example = "y3oqhf46pyzuxjbcn2giaqnb44")]
pub merchant_id: Option<String>, pub merchant_id: Option<String>,
/// Total amount for which the refund is to be initiated. Amount for the payment in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc., If not provided, this will default to the full payment amount
#[schema(minimum = 100, example = 6540)]
pub amount: Option<i64>, pub amount: Option<i64>,
/// An arbitrary string attached to the object. Often useful for displaying to users and your customer support executive
#[schema(max_length = 255, example = "Customer returned the product")]
pub reason: Option<String>, pub reason: Option<String>,
/// The type of refund based on waiting time for processing: Scheduled or Instant Refund
#[schema(default = "Instant", example = "Instant")]
pub refund_type: Option<RefundType>, pub refund_type: Option<RefundType>,
/// You can specify up to 50 keys, with key names up to 40 characters long and values up to 500 characters long. Metadata is useful for storing additional, structured information on an object.
#[schema(value_type = Option<Object>, example = r#"{ "city": "NY", "unit": "245" }"#)]
pub metadata: Option<serde_json::Value>, pub metadata: Option<serde_json::Value>,
} }
#[derive(Default, Debug, Clone, Deserialize)] #[derive(Default, Debug, Clone, ToSchema, Deserialize)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub enum RefundType { pub enum RefundType {
#[default] #[default]
@ -24,7 +53,7 @@ pub enum RefundType {
Instant, Instant,
} }
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] #[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize, ToSchema)]
pub struct RefundResponse { pub struct RefundResponse {
pub refund_id: String, pub refund_id: String,
pub payment_id: String, pub payment_id: String,
@ -32,6 +61,7 @@ pub struct RefundResponse {
pub currency: String, pub currency: String,
pub reason: Option<String>, pub reason: Option<String>,
pub status: RefundStatus, pub status: RefundStatus,
#[schema(value_type = Option<Object>)]
pub metadata: Option<serde_json::Value>, pub metadata: Option<serde_json::Value>,
pub error_message: Option<String>, pub error_message: Option<String>,
} }
@ -65,7 +95,7 @@ pub struct RefundListResponse {
pub data: Vec<RefundResponse>, pub data: Vec<RefundResponse>,
} }
#[derive(Debug, Eq, Clone, PartialEq, Default, Deserialize, Serialize)] #[derive(Debug, Eq, Clone, PartialEq, Default, Deserialize, Serialize, ToSchema)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub enum RefundStatus { pub enum RefundStatus {
Succeeded, Succeeded,

View File

@ -65,6 +65,7 @@ thiserror = "1.0.37"
time = { version = "0.3.17", features = ["serde", "serde-well-known", "std"] } time = { version = "0.3.17", features = ["serde", "serde-well-known", "std"] }
tokio = { version = "1.23.0", features = ["macros", "rt-multi-thread"] } tokio = { version = "1.23.0", features = ["macros", "rt-multi-thread"] }
url = { version = "2.3.1", features = ["serde"] } url = { version = "2.3.1", features = ["serde"] }
utoipa = { git = "https://github.com/SanchithHegde/utoipa", branch = "retain-property-order" }
uuid = { version = "1.2.2", features = ["serde", "v4"] } uuid = { version = "1.2.2", features = ["serde", "v4"] }
# First party crates # First party crates
@ -84,10 +85,10 @@ actix-http = "3.2.2"
awc = { version = "3.0.1", features = ["rustls"] } awc = { version = "3.0.1", features = ["rustls"] }
derive_deref = "1.1.1" derive_deref = "1.1.1"
rand = "0.8.5" rand = "0.8.5"
serial_test = "0.10.0"
time = { version = "0.3.17", features = ["macros"] } time = { version = "0.3.17", features = ["macros"] }
tokio = "1.23.0" tokio = "1.23.0"
toml = "0.5.9" toml = "0.5.9"
serial_test = "0.10.0"
wiremock = "0.5" wiremock = "0.5"
[[bin]] [[bin]]

View File

@ -1,5 +1,5 @@
use router::{ use router::{
configs::settings::{CmdLineConf, Settings}, configs::settings::{CmdLineConf, Settings, Subcommand},
core::errors::{BachError, BachResult}, core::errors::{BachError, BachResult},
logger, logger,
}; };
@ -10,6 +10,20 @@ async fn main() -> BachResult<()> {
// get commandline config before initializing config // get commandline config before initializing config
let cmd_line = CmdLineConf::from_args(); let cmd_line = CmdLineConf::from_args();
if let Some(Subcommand::GenerateOpenapiSpec) = cmd_line.subcommand {
let file_path = "openapi/generated.json";
#[allow(clippy::expect_used)]
std::fs::write(
file_path,
<router::openapi::ApiDoc as utoipa::OpenApi>::openapi()
.to_pretty_json()
.expect("Failed to generate serialize OpenAPI specification as JSON"),
)
.expect("Failed to write OpenAPI specification to file");
println!("Successfully saved OpenAPI specification file at '{file_path}'");
return Ok(());
}
#[allow(clippy::expect_used)] #[allow(clippy::expect_used)]
let conf = Settings::with_config_path(cmd_line.config_path) let conf = Settings::with_config_path(cmd_line.config_path)
.expect("Unable to construct application configuration"); .expect("Unable to construct application configuration");

View File

@ -18,6 +18,15 @@ pub struct CmdLineConf {
/// Application will look for "config/config.toml" if this option isn't specified. /// Application will look for "config/config.toml" if this option isn't specified.
#[structopt(short = "f", long, parse(from_os_str))] #[structopt(short = "f", long, parse(from_os_str))]
pub config_path: Option<PathBuf>, pub config_path: Option<PathBuf>,
#[structopt(subcommand)]
pub subcommand: Option<Subcommand>,
}
#[derive(StructOpt)]
pub enum Subcommand {
/// Generate the OpenAPI specification file from code.
GenerateOpenapiSpec,
} }
#[derive(Debug, Deserialize, Clone)] #[derive(Debug, Deserialize, Clone)]

View File

@ -16,6 +16,7 @@ pub mod routes;
pub mod scheduler; pub mod scheduler;
mod middleware; mod middleware;
pub mod openapi;
pub mod services; pub mod services;
pub mod types; pub mod types;
pub mod utils; pub mod utils;

View File

@ -0,0 +1,67 @@
#[derive(utoipa::OpenApi)]
#[openapi(
info(
title = "Juspay Router - API Documentation",
contact(
name = "Juspay Support",
url = "https://juspay.io",
email = "support@juspay.in"
),
// terms_of_service = "https://www.juspay.io/terms",
description = r#"
## Get started
Juspay Router provides a collection of APIs that enable you to process and manage payments.
Our APIs accept and return JSON in the HTTP body, and return standard HTTP response codes.
You can consume the APIs directly using your favorite HTTP/REST library.
We have a testing environment referred to "sandbox", which you can setup to test API calls without
affecting production data.
### Base URLs
Use the following base URLs when making requests to the APIs:
| Environment | Base URL |
|---------------|------------------------------------------------------|
| Sandbox | <https://sandbox-router.juspay.io> |
| Production | <https://router.juspay.io> |
## Authentication
When you sign up on our [dashboard](https://orca-dahboard.netlify.app) and create a merchant
account, you are given a secret key (also referred as api-key).
You may authenticate all API requests with Juspay server by providing the appropriate key in the
request Authorization header.
Never share your secret api keys. Keep them guarded and secure.
"#,
),
servers(
(url = "https://sandbox-router.juspay.io", description = "Sandbox Environment"),
(url = "https://router.juspay.io", description = "Production Environment")
),
paths(
crate::routes::refunds::refunds_create,
crate::routes::admin::merchant_account_create
),
components(schemas(
crate::types::api::refunds::RefundRequest,
crate::types::api::refunds::RefundType,
crate::types::api::refunds::RefundResponse,
crate::types::api::refunds::RefundStatus,
crate::types::api::admin::CreateMerchantAccount,
crate::types::api::admin::CustomRoutingRules,
api_models::enums::RoutingAlgorithm,
api_models::enums::PaymentMethodType,
api_models::enums::PaymentMethodSubType,
api_models::enums::Currency,
api_models::payments::AddressDetails,
crate::types::api::admin::MerchantAccountResponse,
crate::types::api::admin::MerchantConnectorId,
crate::types::api::admin::MerchantDetails,
crate::types::api::admin::WebhookDetails,
))
)]
pub struct ApiDoc;

View File

@ -4,6 +4,18 @@ use router_env::{instrument, tracing, Flow};
use super::app::AppState; use super::app::AppState;
use crate::{core::admin::*, services::api, types::api::admin}; use crate::{core::admin::*, services::api, types::api::admin};
/// Merchant Account - Create
///
/// 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.
#[utoipa::path(
post,
path = "/account",
request_body= CreateMerchantAccount,
responses(
(status = 200, description = "Merchant Account Created", body = MerchantAccountResponse),
(status = 400, description = "Invalid data")
)
)]
#[instrument(skip_all, fields(flow = ?Flow::MerchantsAccountCreate))] #[instrument(skip_all, fields(flow = ?Flow::MerchantsAccountCreate))]
// #[post("")] // #[post("")]
pub async fn merchant_account_create( pub async fn merchant_account_create(

View File

@ -4,6 +4,18 @@ use router_env::{instrument, tracing, Flow};
use super::app::AppState; use super::app::AppState;
use crate::{core::refunds::*, services::api, types::api::refunds}; use crate::{core::refunds::*, services::api, types::api::refunds};
/// Refunds - Create
///
/// To create a refund against an already processed payment
#[utoipa::path(
post,
path = "/refunds",
request_body=RefundRequest,
responses(
(status = 200, description = "Refund created", body = RefundResponse),
(status = 400, description = "Missing Mandatory fields")
)
)]
#[instrument(skip_all, fields(flow = ?Flow::RefundsCreate))] #[instrument(skip_all, fields(flow = ?Flow::RefundsCreate))]
// #[post("")] // #[post("")]
pub async fn refunds_create( pub async fn refunds_create(

View File

@ -1,4 +1,4 @@
pub use api_models::refunds::{RefundRequest, RefundResponse, RefundStatus}; pub use api_models::refunds::{RefundRequest, RefundResponse, RefundStatus, RefundType};
use super::ConnectorCommon; use super::ConnectorCommon;
use crate::{ use crate::{

855
openapi/generated.json Normal file
View File

@ -0,0 +1,855 @@
{
"openapi": "3.0.3",
"info": {
"title": "Juspay Router - API Documentation",
"description": "\n## Get started\n\nJuspay Router provides a collection of APIs that enable you to process and manage payments.\nOur APIs accept and return JSON in the HTTP body, and return standard HTTP response codes.\n\nYou can consume the APIs directly using your favorite HTTP/REST library.\n\nWe have a testing environment referred to \"sandbox\", which you can setup to test API calls without\naffecting production data.\n\n### Base URLs\n\nUse the following base URLs when making requests to the APIs:\n\n| Environment | Base URL |\n|---------------|------------------------------------------------------|\n| Sandbox | <https://sandbox-router.juspay.io> |\n| Production | <https://router.juspay.io> |\n\n## Authentication\n\nWhen you sign up on our [dashboard](https://orca-dahboard.netlify.app) and create a merchant\naccount, you are given a secret key (also referred as api-key).\nYou may authenticate all API requests with Juspay server by providing the appropriate key in the\nrequest Authorization header.\n\nNever share your secret api keys. Keep them guarded and secure.\n",
"contact": {
"name": "Juspay Support",
"url": "https://juspay.io",
"email": "support@juspay.in"
},
"license": {
"name": "Apache-2.0"
},
"version": "0.2.0"
},
"servers": [
{
"url": "https://sandbox-router.juspay.io",
"description": "Sandbox Environment"
},
{
"url": "https://router.juspay.io",
"description": "Production Environment"
}
],
"paths": {
"/account": {
"post": {
"tags": [
"crate::routes::admin"
],
"summary": "Merchant Account - Create",
"description": "Merchant Account - Create\n\nCreate a new account for a merchant and the merchant could be a seller or retailer or client who likes to receive and send payments.",
"operationId": "merchant_account_create",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreateMerchantAccount"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "Merchant Account Created",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MerchantAccountResponse"
}
}
}
},
"400": {
"description": "Invalid data"
}
},
"deprecated": false
}
},
"/refunds": {
"post": {
"tags": [
"crate::routes::refunds"
],
"summary": "Refunds - Create",
"description": "Refunds - Create\n\nTo create a refund against an already processed payment",
"operationId": "refunds_create",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RefundRequest"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "Refund created",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RefundResponse"
}
}
}
},
"400": {
"description": "Missing Mandatory fields"
}
},
"deprecated": false
}
}
},
"components": {
"schemas": {
"AddressDetails": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "The address city",
"example": "New York",
"maxLength": 50
},
"country": {
"type": "string",
"description": "The two-letter ISO country code for the address",
"example": "US",
"maxLength": 2,
"minLength": 2
},
"line1": {
"type": "string",
"description": "The first line of the address",
"example": "123, King Street",
"maxLength": 200
},
"line2": {
"type": "string",
"description": "The second line of the address",
"example": "Powelson Avenue",
"maxLength": 50
},
"line3": {
"type": "string",
"description": "The third line of the address",
"example": "Bridgewater",
"maxLength": 50
},
"zip": {
"type": "string",
"description": "The zip/postal code for the address",
"example": "08807",
"maxLength": 50
},
"state": {
"type": "string",
"description": "The address state",
"example": "New York"
},
"first_name": {
"type": "string",
"description": "The first name for the address",
"example": "John",
"maxLength": 255
},
"last_name": {
"type": "string",
"description": "The last name for the address",
"example": "Doe",
"maxLength": 255
}
}
},
"CreateMerchantAccount": {
"type": "object",
"required": [
"merchant_id"
],
"properties": {
"merchant_id": {
"type": "string",
"description": "The identifier for the Merchant Account",
"example": "y3oqhf46pyzuxjbcn2giaqnb44",
"maxLength": 255
},
"merchant_name": {
"type": "string",
"description": "Name of the Merchant Account",
"example": "NewAge Retailer"
},
"api_key": {
"type": "string",
"description": "API key that will be used for server side API access",
"example": "Ah2354543543523"
},
"merchant_details": {
"$ref": "#/components/schemas/MerchantDetails"
},
"return_url": {
"type": "string",
"description": "The URL to redirect after the completion of the operation",
"example": "www.example.com/success",
"maxLength": 255
},
"webhook_details": {
"$ref": "#/components/schemas/WebhookDetails"
},
"routing_algorithm": {
"$ref": "#/components/schemas/RoutingAlgorithm"
},
"custom_routing_rules": {
"type": "array",
"items": {
"$ref": "#/components/schemas/CustomRoutingRules"
}
},
"sub_merchants_enabled": {
"type": "boolean",
"description": "A boolean value to indicate if the merchant is a sub-merchant under a master or a parent merchant. By default, its value is false.",
"default": false,
"example": false
},
"parent_merchant_id": {
"type": "string",
"description": "Refers to the Parent Merchant ID if the merchant being created is a sub-merchant",
"example": "xkkdf909012sdjki2dkh5sdf",
"maxLength": 255
},
"enable_payment_response_hash": {
"type": "boolean",
"description": "A boolean value to indicate if payment response hash needs to be enabled",
"default": false,
"example": true
},
"payment_response_hash_key": {
"type": "string",
"description": "Refers to the hash key used for payment response"
},
"redirect_to_merchant_with_http_post": {
"type": "boolean",
"description": "A boolean value to indicate if redirect to merchant with http post needs to be enabled",
"default": false,
"example": true
},
"metadata": {
"type": "object"
},
"publishable_key": {
"type": "string",
"description": "API key that will be used for server side API access",
"example": "AH3423bkjbkjdsfbkj"
}
}
},
"Currency": {
"type": "string",
"enum": [
"AED",
"ALL",
"AMD",
"ARS",
"AUD",
"AWG",
"AZN",
"BBD",
"BDT",
"BHD",
"BMD",
"BND",
"BOB",
"BRL",
"BSD",
"BWP",
"BZD",
"CAD",
"CHF",
"CNY",
"COP",
"CRC",
"CUP",
"CZK",
"DKK",
"DOP",
"DZD",
"EGP",
"ETB",
"EUR",
"FJD",
"GBP",
"GHS",
"GIP",
"GMD",
"GTQ",
"GYD",
"HKD",
"HNL",
"HRK",
"HTG",
"HUF",
"IDR",
"ILS",
"INR",
"JMD",
"JOD",
"JPY",
"KES",
"KGS",
"KHR",
"KRW",
"KWD",
"KYD",
"KZT",
"LAK",
"LBP",
"LKR",
"LRD",
"LSL",
"MAD",
"MDL",
"MKD",
"MMK",
"MNT",
"MOP",
"MUR",
"MVR",
"MWK",
"MXN",
"MYR",
"NAD",
"NGN",
"NIO",
"NOK",
"NPR",
"NZD",
"OMR",
"PEN",
"PGK",
"PHP",
"PKR",
"PLN",
"QAR",
"RUB",
"SAR",
"SCR",
"SEK",
"SGD",
"SLL",
"SOS",
"SSP",
"SVC",
"SZL",
"THB",
"TTD",
"TWD",
"TZS",
"USD",
"UYU",
"UZS",
"YER",
"ZAR"
]
},
"CustomRoutingRules": {
"type": "object",
"properties": {
"payment_methods_incl": {
"type": "array",
"items": {
"$ref": "#/components/schemas/PaymentMethodType"
},
"example": [
"card",
"upi"
]
},
"payment_methods_excl": {
"type": "array",
"items": {
"$ref": "#/components/schemas/PaymentMethodType"
},
"example": [
"card",
"upi"
]
},
"payment_method_types_incl": {
"type": "array",
"items": {
"$ref": "#/components/schemas/PaymentMethodSubType"
},
"example": [
"credit_card",
"debit_card"
]
},
"payment_method_types_excl": {
"type": "array",
"items": {
"$ref": "#/components/schemas/PaymentMethodSubType"
},
"example": [
"credit_card",
"debit_card"
]
},
"payment_method_issuers_incl": {
"type": "array",
"items": {
"type": "string",
"description": "The List of payment method issuers to include for this routing rule"
},
"example": [
"Citibank",
"JPMorgan"
]
},
"payment_method_issuers_excl": {
"type": "array",
"items": {
"type": "string",
"description": "The List of payment method issuers to exclude for this routing rule. If there is conflict between include and exclude lists, include list overrides the exclude list."
},
"example": [
"Citibank",
"JPMorgan"
]
},
"countries_incl": {
"type": "array",
"items": {
"type": "string",
"description": "The List of countries to include for this routing rule"
},
"example": [
"US",
"UK"
]
},
"countries_excl": {
"type": "array",
"items": {
"type": "string",
"description": "The List of countries to exclude for this routing rule. If there is conflict between include and exclude lists, include list overrides the exclude list."
},
"example": [
"US",
"UK"
]
},
"currencies_incl": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Currency"
},
"example": [
"EUR",
"USD"
]
},
"currencies_excl": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Currency"
},
"example": [
"EUR",
"USD"
]
},
"metadata_filters_keys": {
"type": "array",
"items": {
"type": "string",
"description": "List of Metadata Filter keys to apply for the Routing Rule. The filters are presented as 2 arrays of keys and value. This property contains all the keys."
},
"example": [
"platform",
"Category"
]
},
"metadata_filters_values": {
"type": "array",
"items": {
"type": "string",
"description": "List of Metadata Filters to apply for the Routing Rule. The filters are presented as 2 arrays of keys and value. This property contains all the values."
},
"example": [
"android",
"Category_Electronics"
]
},
"connectors_pecking_order": {
"type": "array",
"items": {
"type": "string",
"description": "The pecking order of payment connectors (or processors) to be used for routing. The first connector in the array will be attempted for routing. If it fails, the second connector will be used till the list is exhausted."
},
"example": [
"stripe",
"adyen",
"brain_tree"
]
},
"connectors_traffic_weightage_key": {
"type": "array",
"items": {
"type": "string",
"description": "An Array of Connectors (as Keys) with the associated percentage of traffic to be routed through the given connector (Expressed as an array of values)"
},
"example": [
"stripe",
"adyen",
"brain_tree"
]
},
"connectors_traffic_weightage_value": {
"type": "array",
"items": {
"type": "integer",
"format": "int32",
"description": "An Array of Weightage (expressed in percentage) that needs to be associated with the respective connectors (Expressed as an array of keys)"
},
"example": [
50,
30,
20
]
}
}
},
"MerchantAccountResponse": {
"type": "object",
"required": [
"merchant_id"
],
"properties": {
"merchant_id": {
"type": "string",
"description": "The identifier for the Merchant Account",
"example": "y3oqhf46pyzuxjbcn2giaqnb44",
"maxLength": 255
},
"merchant_name": {
"type": "string",
"description": "Name of the Merchant Account",
"example": "NewAge Retailer"
},
"api_key": {
"type": "string",
"description": "API key that will be used for server side API access",
"example": "Ah2354543543523"
},
"merchant_details": {
"$ref": "#/components/schemas/MerchantDetails"
},
"return_url": {
"type": "string",
"description": "The URL to redirect after the completion of the operation",
"example": "www.example.com/success",
"maxLength": 255
},
"webhook_details": {
"$ref": "#/components/schemas/WebhookDetails"
},
"routing_algorithm": {
"$ref": "#/components/schemas/RoutingAlgorithm"
},
"custom_routing_rules": {
"type": "array",
"items": {
"$ref": "#/components/schemas/CustomRoutingRules"
}
},
"sub_merchants_enabled": {
"type": "boolean",
"description": "A boolean value to indicate if the merchant is a sub-merchant under a master or a parent merchant. By default, its value is false.",
"default": false,
"example": false
},
"parent_merchant_id": {
"type": "string",
"description": "Refers to the Parent Merchant ID if the merchant being created is a sub-merchant",
"example": "xkkdf909012sdjki2dkh5sdf",
"maxLength": 255
},
"enable_payment_response_hash": {
"type": "boolean",
"description": "A boolean value to indicate if payment response hash needs to be enabled",
"default": false,
"example": true
},
"payment_response_hash_key": {
"type": "string",
"description": "Refers to the hash key used for payment response"
},
"redirect_to_merchant_with_http_post": {
"type": "boolean",
"description": "A boolean value to indicate if redirect to merchant with http post needs to be enabled",
"default": false,
"example": true
},
"metadata": {
"type": "object"
},
"publishable_key": {
"type": "string",
"description": "API key that will be used for server side API access",
"example": "AH3423bkjbkjdsfbkj"
}
}
},
"MerchantConnectorId": {
"type": "object",
"required": [
"merchant_id",
"merchant_connector_id"
],
"properties": {
"merchant_id": {
"type": "string"
},
"merchant_connector_id": {
"type": "integer",
"format": "int32"
}
}
},
"MerchantDetails": {
"type": "object",
"properties": {
"primary_contact_person": {
"type": "string",
"description": "The merchant's primary contact name",
"example": "John Doe",
"maxLength": 255
},
"primary_phone": {
"type": "string",
"description": "The merchant's primary phone number",
"example": "999999999",
"maxLength": 255
},
"primary_email": {
"type": "string",
"description": "The merchant's primary email address",
"example": "johndoe@test.com",
"maxLength": 255
},
"secondary_contact_person": {
"type": "string",
"description": "The merchant's secondary contact name",
"example": "John Doe2",
"maxLength": 255
},
"secondary_phone": {
"type": "string",
"description": "The merchant's secondary phone number",
"example": "999999988",
"maxLength": 255
},
"secondary_email": {
"type": "string",
"description": "The merchant's secondary email address",
"example": "johndoe2@test.com",
"maxLength": 255
},
"website": {
"type": "string",
"description": "The business website of the merchant",
"example": "www.example.com",
"maxLength": 255
},
"about_business": {
"type": "string",
"description": "A brief description about merchant's business",
"example": "Online Retail with a wide selection of organic products for North America",
"maxLength": 255
},
"address": {
"$ref": "#/components/schemas/AddressDetails"
}
}
},
"PaymentMethodSubType": {
"type": "string",
"enum": [
"credit",
"debit",
"upi_intent",
"upi_collect",
"credit_card_installments",
"pay_later_installments"
]
},
"PaymentMethodType": {
"type": "string",
"enum": [
"card",
"payment_container",
"bank_transfer",
"bank_debit",
"pay_later",
"netbanking",
"upi",
"open_banking",
"consumer_finance",
"wallet",
"klarna",
"paypal"
]
},
"RefundRequest": {
"type": "object",
"required": [
"payment_id"
],
"properties": {
"refund_id": {
"type": "string",
"description": "Unique Identifier for the Refund. This is to ensure idempotency for multiple partial refund initiated against the same payment. If the identifiers is not defined by the merchant, this filed shall be auto generated and provide in the API response. It is recommended to generate uuid(v4) as the refund_id.",
"example": "ref_mbabizu24mvu3mela5njyhpit4",
"maxLength": 30,
"minLength": 30
},
"payment_id": {
"type": "string",
"description": "Total amount for which the refund is to be initiated. Amount for the payment in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc. If not provided, this will default to the full payment amount",
"example": "pay_mbabizu24mvu3mela5njyhpit4",
"maxLength": 30,
"minLength": 30
},
"merchant_id": {
"type": "string",
"description": "The identifier for the Merchant Account",
"example": "y3oqhf46pyzuxjbcn2giaqnb44",
"maxLength": 255
},
"amount": {
"type": "integer",
"format": "int64",
"description": "Total amount for which the refund is to be initiated. Amount for the payment in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc., If not provided, this will default to the full payment amount",
"example": 6540,
"minimum": 100.0
},
"reason": {
"type": "string",
"description": "An arbitrary string attached to the object. Often useful for displaying to users and your customer support executive",
"example": "Customer returned the product",
"maxLength": 255
},
"refund_type": {
"$ref": "#/components/schemas/RefundType"
},
"metadata": {
"type": "object"
}
}
},
"RefundResponse": {
"type": "object",
"required": [
"refund_id",
"payment_id",
"amount",
"currency",
"status"
],
"properties": {
"refund_id": {
"type": "string"
},
"payment_id": {
"type": "string"
},
"amount": {
"type": "integer",
"format": "int64"
},
"currency": {
"type": "string"
},
"reason": {
"type": "string"
},
"status": {
"$ref": "#/components/schemas/RefundStatus"
},
"metadata": {
"type": "object"
},
"error_message": {
"type": "string"
}
}
},
"RefundStatus": {
"type": "string",
"enum": [
"succeeded",
"failed",
"pending",
"review"
]
},
"RefundType": {
"type": "string",
"enum": [
"scheduled",
"instant"
]
},
"RoutingAlgorithm": {
"type": "string",
"description": "The routing algorithm to be used to process the incoming request from merchant to outgoing payment processor or payment method. The default is 'Custom'",
"enum": [
"round_robin",
"max_conversion",
"min_cost",
"custom"
],
"example": "custom"
},
"WebhookDetails": {
"type": "object",
"properties": {
"webhook_version": {
"type": "string",
"description": "The version for Webhook",
"example": "1.0.2",
"maxLength": 255
},
"webhook_username": {
"type": "string",
"description": "The user name for Webhook login",
"example": "ekart_retail",
"maxLength": 255
},
"webhook_password": {
"type": "string",
"description": "The password for Webhook login",
"example": "ekart@123",
"maxLength": 255
},
"webhook_url": {
"type": "string",
"description": "The url for the webhook endpoint",
"example": "www.ekart.com/webhooks"
},
"payment_created_enabled": {
"type": "boolean",
"description": "If this property is true, a webhook message is posted whenever a new payment is created",
"example": true
},
"payment_succeeded_enabled": {
"type": "boolean",
"description": "If this property is true, a webhook message is posted whenever a payment is successful",
"example": true
},
"payment_failed_enabled": {
"type": "boolean",
"description": "If this property is true, a webhook message is posted whenever a payment fails",
"example": true
}
}
}
}
}
}