diff --git a/Core/Resgrid.Model/Billing/Api/CreatePaddleCheckoutResult.cs b/Core/Resgrid.Model/Billing/Api/CreatePaddleCheckoutResult.cs index f8533bee..fa2a8a55 100644 --- a/Core/Resgrid.Model/Billing/Api/CreatePaddleCheckoutResult.cs +++ b/Core/Resgrid.Model/Billing/Api/CreatePaddleCheckoutResult.cs @@ -7,6 +7,7 @@ public class CreatePaddleCheckoutResult : BillingApiResponseBase public class CreatePaddleCheckoutData { + public string TransactionId { get; set; } public string PriceId { get; set; } public string CustomerId { get; set; } public string Environment { get; set; } diff --git a/Core/Resgrid.Model/Services/ISubscriptionsService.cs b/Core/Resgrid.Model/Services/ISubscriptionsService.cs index ce9e787b..a06f7693 100644 --- a/Core/Resgrid.Model/Services/ISubscriptionsService.cs +++ b/Core/Resgrid.Model/Services/ISubscriptionsService.cs @@ -250,7 +250,7 @@ Task CreateStripeSessionForCustomerPortal( Task ChangeActiveSubscriptionAsync(string stripeCustomerId, string stripePlanId); - Task CreatePaddleCheckoutForSub(int departmentId, string paddleCustomerId, string paddlePriceId, int planId, string email, string departmentName, int count, string discountCode = null); + Task CreatePaddleCheckoutForSub(int departmentId, string paddleCustomerId, string paddleProductId, int planId, string email, string departmentName, int count, string discountCode = null); Task CreatePaddleCheckoutForUpdate(int departmentId, string paddleCustomerId, string email, string departmentName); diff --git a/Core/Resgrid.Services/SubscriptionsService.cs b/Core/Resgrid.Services/SubscriptionsService.cs index ece7d93c..53d356d4 100644 --- a/Core/Resgrid.Services/SubscriptionsService.cs +++ b/Core/Resgrid.Services/SubscriptionsService.cs @@ -1220,7 +1220,7 @@ public async Task ChangeActiveSubscriptionAsync(st return null; } - public async Task CreatePaddleCheckoutForSub(int departmentId, string paddleCustomerId, string paddlePriceId, int planId, string email, string departmentName, int count, string discountCode = null) + public async Task CreatePaddleCheckoutForSub(int departmentId, string paddleCustomerId, string paddleProductId, int planId, string email, string departmentName, int count, string discountCode = null) { if (!String.IsNullOrWhiteSpace(Config.SystemBehaviorConfig.BillingApiBaseUrl) && !String.IsNullOrWhiteSpace(Config.ApiConfig.BackendInternalApikey)) { @@ -1233,7 +1233,8 @@ public async Task CreatePaddleCheckoutForSub(int depar request.AddHeader("Content-Type", "application/json"); request.AddParameter("paddleCustomerId", Uri.EscapeDataString(paddleCustomerId), ParameterType.QueryString); request.AddParameter("departmentId", departmentId, ParameterType.QueryString); - request.AddParameter("paddlePriceId", paddlePriceId, ParameterType.QueryString); + request.AddParameter("paddleProductId", paddleProductId, ParameterType.QueryString); + request.AddParameter("paddlePriceId", paddleProductId, ParameterType.QueryString); request.AddParameter("planId", planId, ParameterType.QueryString); request.AddParameter("count", count, ParameterType.QueryString); request.AddParameter("email", email, ParameterType.QueryString, true); diff --git a/Web/Resgrid.Web/Areas/User/Controllers/SubscriptionController.cs b/Web/Resgrid.Web/Areas/User/Controllers/SubscriptionController.cs index 3720c5eb..38b7ba09 100644 --- a/Web/Resgrid.Web/Areas/User/Controllers/SubscriptionController.cs +++ b/Web/Resgrid.Web/Areas/User/Controllers/SubscriptionController.cs @@ -112,6 +112,11 @@ private static string GetPaddleConfigurationError(string paddleEnvironment, stri return null; } + private static string GetPaddleCheckoutProductId(Resgrid.Model.Plan plan) + { + return plan?.GetExternalKey() ?? string.Empty; + } + [HttpGet] [Authorize] public async Task SelectRegistrationPlan(string discountCode = null) @@ -782,10 +787,15 @@ public async Task GetPaddleCheckout(int id, int count, string dis return BadRequest("Invalid entity pack count."); var plan = await _subscriptionsService.GetPlanByIdAsync(id); + var paddleProductId = GetPaddleCheckoutProductId(plan); + + if (string.IsNullOrWhiteSpace(paddleProductId)) + return StatusCode(StatusCodes.Status500InternalServerError, "Paddle checkout is not configured for this plan."); + var paddleCustomerId = await _departmentSettingsService.GetPaddleCustomerIdForDepartmentAsync(DepartmentId); var department = await _departmentsService.GetDepartmentByIdAsync(DepartmentId); var user = _usersService.GetUserById(UserId); - var checkout = await _subscriptionsService.CreatePaddleCheckoutForSub(DepartmentId, paddleCustomerId, plan.GetExternalKey(), plan.PlanId, user.Email, department.Name, count, discountCode); + var checkout = await _subscriptionsService.CreatePaddleCheckoutForSub(DepartmentId, paddleCustomerId, paddleProductId, plan.PlanId, user.Email, department.Name, count, discountCode); bool hasActiveSub = false; if (!string.IsNullOrWhiteSpace(paddleCustomerId)) @@ -797,6 +807,7 @@ public async Task GetPaddleCheckout(int id, int count, string dis return Json(new { + TransactionId = checkout?.TransactionId, PriceId = checkout?.PriceId, CustomerId = checkout?.CustomerId, Environment = checkout?.Environment, diff --git a/Web/Resgrid.Web/Areas/User/Views/Subscription/Index.cshtml b/Web/Resgrid.Web/Areas/User/Views/Subscription/Index.cshtml index 0cb7a4e2..ef85ed0d 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Subscription/Index.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Subscription/Index.cshtml @@ -606,10 +606,22 @@ return; } + if (data.TransactionId) { + Paddle.Checkout.open({ + settings: { successUrl: resgrid.absoluteBaseUrl + '/User/Subscription/PaddleProcessing?planId=' + id }, + transactionId: data.TransactionId + }); + return; + } + if (!data.PriceId) { swal({ title: "Checkout Error", text: "Unable to create a checkout session. Please try again.", icon: "error", buttons: true, dangerMode: false }); return; } + if (!/^pri_/.test(data.PriceId)) { + swal({ title: "Checkout Error", text: "Paddle checkout returned an unsupported checkout payload. Please contact support.", icon: "error", buttons: true, dangerMode: false }); + return; + } var checkoutSettings = { settings: { successUrl: resgrid.absoluteBaseUrl + '/User/Subscription/PaddleProcessing?planId=' + id }, diff --git a/Web/Resgrid.Web/Areas/User/Views/Subscription/SelectRegistrationPlan.cshtml b/Web/Resgrid.Web/Areas/User/Views/Subscription/SelectRegistrationPlan.cshtml index 2983b22e..52375f1a 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Subscription/SelectRegistrationPlan.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Subscription/SelectRegistrationPlan.cshtml @@ -222,10 +222,21 @@ swal({ title: "Active Subscription", text: "You already have an active subscription.", icon: "warning", buttons: true, dangerMode: false }); return; } + if (data.TransactionId) { + Paddle.Checkout.open({ + settings: { successUrl: resgrid.absoluteBaseUrl + '/User/Subscription/PaddleProcessing?planId=' + id }, + transactionId: data.TransactionId + }); + return; + } if (!data.PriceId) { swal({ title: "Checkout Error", text: "Unable to create a checkout session. Please try again.", icon: "error", buttons: true, dangerMode: false }); return; } + if (!/^pri_/.test(data.PriceId)) { + swal({ title: "Checkout Error", text: "Paddle checkout returned an unsupported checkout payload. Please contact support.", icon: "error", buttons: true, dangerMode: false }); + return; + } var checkoutSettings = { settings: { successUrl: resgrid.absoluteBaseUrl + '/User/Subscription/PaddleProcessing?planId=' + id }, items: [{ priceId: data.PriceId, quantity: packs }]