mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-27 19:46:48 +08:00
feat(subscriptions): Add client secret auth support in subscriptions APIs (#9713)
Co-authored-by: Prajjwal kumar <write2prajjwal@gmail.com> Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Co-authored-by: Prajjwal Kumar <prajjwal.kumar@juspay.in> Co-authored-by: Jagan <jaganelavarasan@gmail.com>
This commit is contained in:
@ -74,6 +74,12 @@ pub struct SubscriptionResponse {
|
||||
|
||||
/// Optional customer ID associated with this subscription.
|
||||
pub customer_id: common_utils::id_type::CustomerId,
|
||||
|
||||
/// Payment details for the invoice.
|
||||
pub payment: Option<PaymentResponseData>,
|
||||
|
||||
/// Invoice Details for the subscription.
|
||||
pub invoice: Option<Invoice>,
|
||||
}
|
||||
|
||||
/// Possible states of a subscription lifecycle.
|
||||
@ -127,6 +133,8 @@ impl SubscriptionResponse {
|
||||
merchant_id: common_utils::id_type::MerchantId,
|
||||
client_secret: Option<Secret<String>>,
|
||||
customer_id: common_utils::id_type::CustomerId,
|
||||
payment: Option<PaymentResponseData>,
|
||||
invoice: Option<Invoice>,
|
||||
) -> Self {
|
||||
Self {
|
||||
id,
|
||||
@ -138,6 +146,8 @@ impl SubscriptionResponse {
|
||||
merchant_id,
|
||||
coupon_code: None,
|
||||
customer_id,
|
||||
payment,
|
||||
invoice,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -181,6 +191,10 @@ impl ClientSecret {
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
|
||||
pub fn as_string(&self) -> &String {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize, Debug)]
|
||||
@ -197,6 +211,7 @@ impl ApiEventMetric for GetPlansResponse {}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||
pub struct ConfirmSubscriptionPaymentDetails {
|
||||
pub shipping: Option<Address>,
|
||||
pub payment_method: api_enums::PaymentMethod,
|
||||
pub payment_method_type: Option<api_enums::PaymentMethodType>,
|
||||
pub payment_method_data: PaymentMethodDataRequest,
|
||||
@ -278,7 +293,7 @@ pub struct PaymentResponseData {
|
||||
pub error_code: Option<String>,
|
||||
pub error_message: Option<String>,
|
||||
pub payment_method_type: Option<api_enums::PaymentMethodType>,
|
||||
pub client_secret: Option<String>,
|
||||
pub client_secret: Option<Secret<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||
@ -294,7 +309,7 @@ pub struct CreateMitPaymentRequestData {
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||
pub struct ConfirmSubscriptionRequest {
|
||||
/// Client secret for SDK based interaction.
|
||||
pub client_secret: Option<String>,
|
||||
pub client_secret: Option<ClientSecret>,
|
||||
|
||||
/// Identifier for the associated plan_id.
|
||||
pub plan_id: Option<String>,
|
||||
@ -305,15 +320,6 @@ pub struct ConfirmSubscriptionRequest {
|
||||
/// Idenctifier for the coupon code for the subscription.
|
||||
pub coupon_code: Option<String>,
|
||||
|
||||
/// Identifier for customer.
|
||||
pub customer_id: common_utils::id_type::CustomerId,
|
||||
|
||||
/// Billing address for the subscription.
|
||||
pub billing: Option<Address>,
|
||||
|
||||
/// Shipping address for the subscription.
|
||||
pub shipping: Option<Address>,
|
||||
|
||||
/// Payment details for the invoice.
|
||||
pub payment_details: ConfirmSubscriptionPaymentDetails,
|
||||
}
|
||||
@ -328,11 +334,15 @@ impl ConfirmSubscriptionRequest {
|
||||
}
|
||||
|
||||
pub fn get_billing_address(&self) -> Result<Address, error_stack::Report<ValidationError>> {
|
||||
self.billing.clone().ok_or(error_stack::report!(
|
||||
ValidationError::MissingRequiredField {
|
||||
field_name: "billing".to_string()
|
||||
}
|
||||
))
|
||||
self.payment_details
|
||||
.payment_method_data
|
||||
.billing
|
||||
.clone()
|
||||
.ok_or(error_stack::report!(
|
||||
ValidationError::MissingRequiredField {
|
||||
field_name: "billing".to_string()
|
||||
}
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -23,6 +23,7 @@ pub struct InvoiceNew {
|
||||
pub metadata: Option<SecretSerdeValue>,
|
||||
pub created_at: time::PrimitiveDateTime,
|
||||
pub modified_at: time::PrimitiveDateTime,
|
||||
pub connector_invoice_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
@ -49,6 +50,7 @@ pub struct Invoice {
|
||||
pub metadata: Option<SecretSerdeValue>,
|
||||
pub created_at: time::PrimitiveDateTime,
|
||||
pub modified_at: time::PrimitiveDateTime,
|
||||
pub connector_invoice_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, AsChangeset, Deserialize)]
|
||||
@ -56,6 +58,7 @@ pub struct Invoice {
|
||||
pub struct InvoiceUpdate {
|
||||
pub status: Option<String>,
|
||||
pub payment_method_id: Option<String>,
|
||||
pub connector_invoice_id: Option<String>,
|
||||
pub modified_at: time::PrimitiveDateTime,
|
||||
pub payment_intent_id: Option<common_utils::id_type::PaymentId>,
|
||||
}
|
||||
@ -75,6 +78,7 @@ impl InvoiceNew {
|
||||
status: InvoiceStatus,
|
||||
provider_name: Connector,
|
||||
metadata: Option<SecretSerdeValue>,
|
||||
connector_invoice_id: Option<String>,
|
||||
) -> Self {
|
||||
let id = common_utils::id_type::InvoiceId::generate();
|
||||
let now = common_utils::date_time::now();
|
||||
@ -94,6 +98,7 @@ impl InvoiceNew {
|
||||
metadata,
|
||||
created_at: now,
|
||||
modified_at: now,
|
||||
connector_invoice_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -102,11 +107,13 @@ impl InvoiceUpdate {
|
||||
pub fn new(
|
||||
payment_method_id: Option<String>,
|
||||
status: Option<InvoiceStatus>,
|
||||
connector_invoice_id: Option<String>,
|
||||
payment_intent_id: Option<common_utils::id_type::PaymentId>,
|
||||
) -> Self {
|
||||
Self {
|
||||
payment_method_id,
|
||||
status: status.map(|status| status.to_string()),
|
||||
connector_invoice_id,
|
||||
payment_intent_id,
|
||||
modified_at: common_utils::date_time::now(),
|
||||
}
|
||||
|
||||
@ -750,6 +750,8 @@ diesel::table! {
|
||||
metadata -> Nullable<Jsonb>,
|
||||
created_at -> Timestamp,
|
||||
modified_at -> Timestamp,
|
||||
#[max_length = 64]
|
||||
connector_invoice_id -> Nullable<Varchar>,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -762,6 +762,8 @@ diesel::table! {
|
||||
metadata -> Nullable<Jsonb>,
|
||||
created_at -> Timestamp,
|
||||
modified_at -> Timestamp,
|
||||
#[max_length = 64]
|
||||
connector_invoice_id -> Nullable<Varchar>,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -4,10 +4,7 @@ use api_models::subscription::{
|
||||
use common_enums::connector_enums;
|
||||
use common_utils::id_type::GenerateId;
|
||||
use error_stack::ResultExt;
|
||||
use hyperswitch_domain_models::{
|
||||
api::ApplicationResponse, merchant_context::MerchantContext,
|
||||
router_response_types::subscriptions as subscription_response_types,
|
||||
};
|
||||
use hyperswitch_domain_models::{api::ApplicationResponse, merchant_context::MerchantContext};
|
||||
|
||||
use super::errors::{self, RouterResponse};
|
||||
use crate::{
|
||||
@ -31,7 +28,7 @@ pub async fn create_subscription(
|
||||
merchant_context: MerchantContext,
|
||||
profile_id: common_utils::id_type::ProfileId,
|
||||
request: subscription_types::CreateSubscriptionRequest,
|
||||
) -> RouterResponse<subscription_types::ConfirmSubscriptionResponse> {
|
||||
) -> RouterResponse<SubscriptionResponse> {
|
||||
let subscription_id = common_utils::id_type::SubscriptionId::generate();
|
||||
|
||||
let profile =
|
||||
@ -68,7 +65,7 @@ pub async fn create_subscription(
|
||||
.create_payment_with_confirm_false(subscription.handler.state, &request)
|
||||
.await
|
||||
.attach_printable("subscriptions: failed to create payment")?;
|
||||
let invoice_entry = invoice_handler
|
||||
let invoice = invoice_handler
|
||||
.create_invoice_entry(
|
||||
&state,
|
||||
billing_handler.merchant_connector_id,
|
||||
@ -78,6 +75,7 @@ pub async fn create_subscription(
|
||||
connector_enums::InvoiceStatus::InvoiceCreated,
|
||||
billing_handler.connector_data.connector_name,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.attach_printable("subscriptions: failed to create invoice")?;
|
||||
@ -91,11 +89,7 @@ pub async fn create_subscription(
|
||||
.await
|
||||
.attach_printable("subscriptions: failed to update subscription")?;
|
||||
|
||||
let response = subscription.generate_response(
|
||||
&invoice_entry,
|
||||
&payment,
|
||||
subscription_response_types::SubscriptionStatus::Created,
|
||||
)?;
|
||||
let response = subscription.to_subscription_response(Some(payment), Some(&invoice))?;
|
||||
|
||||
Ok(ApplicationResponse::Json(response))
|
||||
}
|
||||
@ -148,14 +142,12 @@ pub async fn get_subscription_plans(
|
||||
.into_iter()
|
||||
.map(subscription_types::SubscriptionPlanPrices::from)
|
||||
.collect::<Vec<_>>(),
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
Ok(ApplicationResponse::Json(response))
|
||||
}
|
||||
|
||||
/// Creates and confirms a subscription in one operation.
|
||||
/// This method combines the creation and confirmation flow to reduce API calls
|
||||
pub async fn create_and_confirm_subscription(
|
||||
state: SessionState,
|
||||
merchant_context: MerchantContext,
|
||||
@ -163,7 +155,6 @@ pub async fn create_and_confirm_subscription(
|
||||
request: subscription_types::CreateAndConfirmSubscriptionRequest,
|
||||
) -> RouterResponse<subscription_types::ConfirmSubscriptionResponse> {
|
||||
let subscription_id = common_utils::id_type::SubscriptionId::generate();
|
||||
|
||||
let profile =
|
||||
SubscriptionHandler::find_business_profile(&state, &merchant_context, &profile_id)
|
||||
.await
|
||||
@ -240,6 +231,9 @@ pub async fn create_and_confirm_subscription(
|
||||
.unwrap_or(connector_enums::InvoiceStatus::InvoiceCreated),
|
||||
billing_handler.connector_data.connector_name,
|
||||
None,
|
||||
invoice_details
|
||||
.clone()
|
||||
.map(|invoice| invoice.id.get_string_repr().to_string()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -292,13 +286,23 @@ pub async fn confirm_subscription(
|
||||
SubscriptionHandler::find_business_profile(&state, &merchant_context, &profile_id)
|
||||
.await
|
||||
.attach_printable("subscriptions: failed to find business profile")?;
|
||||
let customer =
|
||||
SubscriptionHandler::find_customer(&state, &merchant_context, &request.customer_id)
|
||||
.await
|
||||
.attach_printable("subscriptions: failed to find customer")?;
|
||||
|
||||
let handler = SubscriptionHandler::new(&state, &merchant_context);
|
||||
if let Some(client_secret) = request.client_secret.clone() {
|
||||
handler
|
||||
.find_and_validate_subscription(&client_secret.into())
|
||||
.await?
|
||||
};
|
||||
|
||||
let mut subscription_entry = handler.find_subscription(subscription_id).await?;
|
||||
let customer = SubscriptionHandler::find_customer(
|
||||
&state,
|
||||
&merchant_context,
|
||||
&subscription_entry.subscription.customer_id,
|
||||
)
|
||||
.await
|
||||
.attach_printable("subscriptions: failed to find customer")?;
|
||||
|
||||
let invoice_handler = subscription_entry.get_invoice_handler(profile.clone());
|
||||
let invoice = invoice_handler
|
||||
.get_latest_invoice(&state)
|
||||
@ -331,7 +335,7 @@ pub async fn confirm_subscription(
|
||||
.create_customer_on_connector(
|
||||
&state,
|
||||
subscription.customer_id.clone(),
|
||||
request.billing.clone(),
|
||||
request.payment_details.payment_method_data.billing.clone(),
|
||||
request
|
||||
.payment_details
|
||||
.payment_method_data
|
||||
@ -344,7 +348,7 @@ pub async fn confirm_subscription(
|
||||
&state,
|
||||
subscription,
|
||||
request.item_price_id,
|
||||
request.billing,
|
||||
request.payment_details.payment_method_data.billing,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -359,6 +363,9 @@ pub async fn confirm_subscription(
|
||||
.clone()
|
||||
.and_then(|invoice| invoice.status)
|
||||
.unwrap_or(connector_enums::InvoiceStatus::InvoiceCreated),
|
||||
invoice_details
|
||||
.clone()
|
||||
.map(|invoice| invoice.id.get_string_repr().to_string()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -418,9 +425,9 @@ pub async fn get_subscription(
|
||||
.await
|
||||
.attach_printable("subscriptions: failed to get subscription entry in get_subscription")?;
|
||||
|
||||
Ok(ApplicationResponse::Json(
|
||||
subscription.to_subscription_response(),
|
||||
))
|
||||
let response = subscription.to_subscription_response(None, None)?;
|
||||
|
||||
Ok(ApplicationResponse::Json(response))
|
||||
}
|
||||
|
||||
pub async fn get_estimate(
|
||||
|
||||
@ -44,6 +44,7 @@ impl InvoiceHandler {
|
||||
status: connector_enums::InvoiceStatus,
|
||||
provider_name: connector_enums::Connector,
|
||||
metadata: Option<pii::SecretSerdeValue>,
|
||||
connector_invoice_id: Option<String>,
|
||||
) -> errors::RouterResult<diesel_models::invoice::Invoice> {
|
||||
let invoice_new = diesel_models::invoice::InvoiceNew::new(
|
||||
self.subscription.id.to_owned(),
|
||||
@ -58,6 +59,7 @@ impl InvoiceHandler {
|
||||
status,
|
||||
provider_name,
|
||||
metadata,
|
||||
connector_invoice_id,
|
||||
);
|
||||
|
||||
let invoice = state
|
||||
@ -79,10 +81,12 @@ impl InvoiceHandler {
|
||||
payment_method_id: Option<Secret<String>>,
|
||||
payment_intent_id: Option<common_utils::id_type::PaymentId>,
|
||||
status: connector_enums::InvoiceStatus,
|
||||
connector_invoice_id: Option<String>,
|
||||
) -> errors::RouterResult<diesel_models::invoice::Invoice> {
|
||||
let update_invoice = diesel_models::invoice::InvoiceUpdate::new(
|
||||
payment_method_id.as_ref().map(|id| id.peek()).cloned(),
|
||||
Some(status),
|
||||
connector_invoice_id,
|
||||
payment_intent_id,
|
||||
);
|
||||
state
|
||||
@ -189,8 +193,8 @@ impl InvoiceHandler {
|
||||
) -> errors::RouterResult<subscription_types::PaymentResponseData> {
|
||||
let payment_details = &request.payment_details;
|
||||
let cit_payment_request = subscription_types::ConfirmPaymentsRequestData {
|
||||
billing: request.billing.clone(),
|
||||
shipping: request.shipping.clone(),
|
||||
billing: request.payment_details.payment_method_data.billing.clone(),
|
||||
shipping: request.payment_details.shipping.clone(),
|
||||
payment_method: payment_details.payment_method,
|
||||
payment_method_type: payment_details.payment_method_type,
|
||||
payment_method_data: payment_details.payment_method_data.clone(),
|
||||
|
||||
@ -19,7 +19,7 @@ use crate::{
|
||||
core::{errors::StorageErrorExt, subscription::invoice_handler::InvoiceHandler},
|
||||
db::CustomResult,
|
||||
routes::SessionState,
|
||||
types::domain,
|
||||
types::{domain, transformers::ForeignTryFrom},
|
||||
};
|
||||
|
||||
pub struct SubscriptionHandler<'a> {
|
||||
@ -227,31 +227,16 @@ impl SubscriptionWithHandler<'_> {
|
||||
price_id: None,
|
||||
coupon: None,
|
||||
billing_processor_subscription_id: self.subscription.connector_subscription_id.clone(),
|
||||
invoice: Some(subscription_types::Invoice {
|
||||
id: invoice.id.clone(),
|
||||
subscription_id: invoice.subscription_id.clone(),
|
||||
merchant_id: invoice.merchant_id.clone(),
|
||||
profile_id: invoice.profile_id.clone(),
|
||||
merchant_connector_id: invoice.merchant_connector_id.clone(),
|
||||
payment_intent_id: invoice.payment_intent_id.clone(),
|
||||
payment_method_id: invoice.payment_method_id.clone(),
|
||||
customer_id: invoice.customer_id.clone(),
|
||||
amount: invoice.amount,
|
||||
currency: api_enums::Currency::from_str(invoice.currency.as_str())
|
||||
.change_context(errors::ApiErrorResponse::InvalidDataValue {
|
||||
field_name: "currency",
|
||||
})
|
||||
.attach_printable(format!(
|
||||
"unable to parse currency name {currency:?}",
|
||||
currency = invoice.currency
|
||||
))?,
|
||||
status: invoice.status.clone(),
|
||||
}),
|
||||
invoice: Some(subscription_types::Invoice::foreign_try_from(invoice)?),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn to_subscription_response(&self) -> SubscriptionResponse {
|
||||
SubscriptionResponse::new(
|
||||
pub fn to_subscription_response(
|
||||
&self,
|
||||
payment: Option<subscription_types::PaymentResponseData>,
|
||||
invoice: Option<&diesel_models::invoice::Invoice>,
|
||||
) -> errors::RouterResult<SubscriptionResponse> {
|
||||
Ok(SubscriptionResponse::new(
|
||||
self.subscription.id.clone(),
|
||||
self.subscription.merchant_reference_id.clone(),
|
||||
SubscriptionStatus::from_str(&self.subscription.status)
|
||||
@ -261,7 +246,15 @@ impl SubscriptionWithHandler<'_> {
|
||||
self.subscription.merchant_id.to_owned(),
|
||||
self.subscription.client_secret.clone().map(Secret::new),
|
||||
self.subscription.customer_id.clone(),
|
||||
)
|
||||
payment,
|
||||
invoice
|
||||
.map(
|
||||
|invoice| -> errors::RouterResult<subscription_types::Invoice> {
|
||||
subscription_types::Invoice::foreign_try_from(invoice)
|
||||
},
|
||||
)
|
||||
.transpose()?,
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn update_subscription(
|
||||
@ -369,3 +362,30 @@ impl SubscriptionWithHandler<'_> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ForeignTryFrom<&diesel_models::invoice::Invoice> for subscription_types::Invoice {
|
||||
type Error = error_stack::Report<errors::ApiErrorResponse>;
|
||||
|
||||
fn foreign_try_from(invoice: &diesel_models::invoice::Invoice) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
id: invoice.id.clone(),
|
||||
subscription_id: invoice.subscription_id.clone(),
|
||||
merchant_id: invoice.merchant_id.clone(),
|
||||
profile_id: invoice.profile_id.clone(),
|
||||
merchant_connector_id: invoice.merchant_connector_id.clone(),
|
||||
payment_intent_id: invoice.payment_intent_id.clone(),
|
||||
payment_method_id: invoice.payment_method_id.clone(),
|
||||
customer_id: invoice.customer_id.clone(),
|
||||
amount: invoice.amount,
|
||||
currency: api_enums::Currency::from_str(invoice.currency.as_str())
|
||||
.change_context(errors::ApiErrorResponse::InvalidDataValue {
|
||||
field_name: "currency",
|
||||
})
|
||||
.attach_printable(format!(
|
||||
"unable to parse currency name {currency:?}",
|
||||
currency = invoice.currency
|
||||
))?,
|
||||
status: invoice.status.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -2645,6 +2645,7 @@ async fn subscription_incoming_webhook_flow(
|
||||
InvoiceStatus::PaymentPending,
|
||||
connector,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -2664,6 +2665,7 @@ async fn subscription_incoming_webhook_flow(
|
||||
payment_response.payment_method_id.clone(),
|
||||
Some(payment_response.payment_id.clone()),
|
||||
InvoiceStatus::from(payment_response.status),
|
||||
Some(mit_payment_data.invoice_id.get_string_repr().to_string()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@ -102,16 +102,25 @@ pub async fn confirm_subscription(
|
||||
) -> impl Responder {
|
||||
let flow = Flow::ConfirmSubscription;
|
||||
let subscription_id = subscription_id.into_inner();
|
||||
let payload = json_payload.into_inner();
|
||||
let profile_id = match extract_profile_id(&req) {
|
||||
Ok(id) => id,
|
||||
Err(response) => return response,
|
||||
};
|
||||
|
||||
let api_auth = auth::ApiKeyAuth::default();
|
||||
|
||||
let (auth_type, _) =
|
||||
match auth::check_client_secret_and_get_auth(req.headers(), &payload, api_auth) {
|
||||
Ok(auth) => auth,
|
||||
Err(err) => return oss_api::log_and_return_error_response(error_stack::report!(err)),
|
||||
};
|
||||
|
||||
Box::pin(oss_api::server_wrap(
|
||||
flow,
|
||||
state,
|
||||
&req,
|
||||
json_payload.into_inner(),
|
||||
payload,
|
||||
|state, auth: auth::AuthenticationData, payload, _| {
|
||||
let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
|
||||
domain::Context(auth.merchant_account, auth.key_store),
|
||||
@ -125,10 +134,7 @@ pub async fn confirm_subscription(
|
||||
)
|
||||
},
|
||||
auth::auth_type(
|
||||
&auth::HeaderAuth(auth::ApiKeyAuth {
|
||||
is_connected_allowed: false,
|
||||
is_platform_allowed: false,
|
||||
}),
|
||||
&*auth_type,
|
||||
&auth::JWTAuth {
|
||||
permission: Permission::ProfileSubscriptionWrite,
|
||||
},
|
||||
@ -147,28 +153,36 @@ pub async fn get_subscription_plans(
|
||||
) -> impl Responder {
|
||||
let flow = Flow::GetPlansForSubscription;
|
||||
let api_auth = auth::ApiKeyAuth::default();
|
||||
let payload = query.into_inner();
|
||||
|
||||
let profile_id = match extract_profile_id(&req) {
|
||||
Ok(profile_id) => profile_id,
|
||||
Err(response) => return response,
|
||||
};
|
||||
|
||||
let auth_data = match auth::is_ephemeral_auth(req.headers(), api_auth) {
|
||||
Ok(auth) => auth,
|
||||
Err(err) => return crate::services::api::log_and_return_error_response(err),
|
||||
};
|
||||
let (auth_type, _) =
|
||||
match auth::check_client_secret_and_get_auth(req.headers(), &payload, api_auth) {
|
||||
Ok(auth) => auth,
|
||||
Err(err) => return oss_api::log_and_return_error_response(error_stack::report!(err)),
|
||||
};
|
||||
Box::pin(oss_api::server_wrap(
|
||||
flow,
|
||||
state,
|
||||
&req,
|
||||
query.into_inner(),
|
||||
payload,
|
||||
|state, auth: auth::AuthenticationData, query, _| {
|
||||
let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
|
||||
domain::Context(auth.merchant_account, auth.key_store),
|
||||
));
|
||||
subscription::get_subscription_plans(state, merchant_context, profile_id.clone(), query)
|
||||
},
|
||||
&*auth_data,
|
||||
auth::auth_type(
|
||||
&*auth_type,
|
||||
&auth::JWTAuth {
|
||||
permission: Permission::ProfileSubscriptionRead,
|
||||
},
|
||||
req.headers(),
|
||||
),
|
||||
api_locking::LockAction::NotApplicable,
|
||||
))
|
||||
.await
|
||||
@ -187,6 +201,7 @@ pub async fn get_subscription(
|
||||
Ok(id) => id,
|
||||
Err(response) => return response,
|
||||
};
|
||||
|
||||
Box::pin(oss_api::server_wrap(
|
||||
flow,
|
||||
state,
|
||||
@ -245,10 +260,16 @@ pub async fn create_and_confirm_subscription(
|
||||
payload.clone(),
|
||||
)
|
||||
},
|
||||
&auth::HeaderAuth(auth::ApiKeyAuth {
|
||||
is_connected_allowed: false,
|
||||
is_platform_allowed: false,
|
||||
}),
|
||||
auth::auth_type(
|
||||
&auth::HeaderAuth(auth::ApiKeyAuth {
|
||||
is_connected_allowed: false,
|
||||
is_platform_allowed: false,
|
||||
}),
|
||||
&auth::JWTAuth {
|
||||
permission: Permission::ProfileSubscriptionWrite,
|
||||
},
|
||||
req.headers(),
|
||||
),
|
||||
api_locking::LockAction::NotApplicable,
|
||||
))
|
||||
.await
|
||||
|
||||
@ -4326,6 +4326,20 @@ impl ClientSecretFetch for api_models::payment_methods::PaymentMethodUpdate {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
impl ClientSecretFetch for api_models::subscription::ConfirmSubscriptionRequest {
|
||||
fn get_client_secret(&self) -> Option<&String> {
|
||||
self.client_secret.as_ref().map(|s| s.as_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
impl ClientSecretFetch for api_models::subscription::GetPlansQuery {
|
||||
fn get_client_secret(&self) -> Option<&String> {
|
||||
self.client_secret.as_ref().map(|s| s.as_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
impl ClientSecretFetch for api_models::authentication::AuthenticationEligibilityRequest {
|
||||
fn get_client_secret(&self) -> Option<&String> {
|
||||
|
||||
@ -204,6 +204,7 @@ impl<'a> InvoiceSyncHandler<'a> {
|
||||
None,
|
||||
None,
|
||||
common_enums::connector_enums::InvoiceStatus::from(invoice_sync_status),
|
||||
Some(connector_invoice_id),
|
||||
)
|
||||
.await
|
||||
.attach_printable("Failed to update invoice in DB")?;
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
-- This file should undo anything in `up.sql`
|
||||
ALTER TABLE invoice DROP COLUMN IF EXISTS connector_invoice_id;
|
||||
DROP INDEX IF EXISTS invoice_subscription_id_connector_invoice_id_index;
|
||||
@ -0,0 +1,3 @@
|
||||
-- Your SQL goes here
|
||||
ALTER TABLE invoice ADD COLUMN IF NOT EXISTS connector_invoice_id VARCHAR(64);
|
||||
CREATE INDEX invoice_subscription_id_connector_invoice_id_index ON invoice (subscription_id, connector_invoice_id);
|
||||
Reference in New Issue
Block a user