Open Closed

Stripe payment module, tenantName resolver issue #6003


User avatar
0
Sergei.Gorlovetsky created
  • ABP Framework version: v7.4.0
  • UI Type: Angular
  • Database System: MongoDB
  • Tiered (for MVC) or Auth Server Separated (for Angular): yes
  • Exception message and full stack trace: Stripe payment gateway, payment request with parameter "success_url": "https://{{tenantName}}.is.clienteleapp.net/Payment/Stripe/PostPayment?SessionId={CHECKOUT_SESSION_ID}" And "cancel_url": "https://{{tenantName}}.is.clienteleapp.net" gives error "The success_url parameter must correspond to a valid URL."
  • Steps to reproduce the issue: We have added the stripe payment module, we are using tenant specific url for both angular and identity server, in appsettings of Identity project self url is configured as "SelfUrl": "https://{{tenantName}}.is.clienteleapp.net". We expect the tenant name should be resolved at the time of PrePayment and PostPayment method of Stripe payment module but it's not resolving the tenant name and causing the error when trying to process payment with stripe payment module.
Markdown supported.
Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)

20 Answer(s)
  • User Avatar
    0
    liangshiwei created
    Fullstack Developer

    Hi,

    I will check it

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    liangshiwei created
    Fullstack Developer

    Hi,

    This looks like a bug. I will fix it and provide you with a temporary solution.

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    liangshiwei created
    Fullstack Developer

    Hi,

    You can try to overwrite the page: https://docs.abp.io/en/abp/latest/UI/AspNetCore/Customization-User-Interface

    public class UrlHelper : ITransientDependency
    {
        private readonly ICurrentTenant _currentTenant;
    
        public UrlHelper(ICurrentTenant currentTenant)
        {
            _currentTenant = currentTenant;
        }
    
        public const string TenantNamePlaceHolder = "{{tenantName}}";
        
        public string ReplaceTenantPlaceholder(string url)
        {
            var tenantNamePlaceHolder = TenantNamePlaceHolder;
    
            if (url.Contains(TenantNamePlaceHolder + '.'))
            {
                tenantNamePlaceHolder = TenantNamePlaceHolder + '.';
            }
    
            if (url.Contains(tenantNamePlaceHolder))
            {
                url = _currentTenant.Id.HasValue ? url.Replace(tenantNamePlaceHolder, _currentTenant.Name + ".") : url.Replace(tenantNamePlaceHolder, "");
            }
    
            return url;
        }
    }
    

    Pages/Payment/GatewaySelection.cshtml

    @page
    @using Microsoft.AspNetCore.Mvc.Localization
    @using Volo.Payment.Localization
    @model Volo.Payment.Pages.Payment.MyGatewaySelectionModel
    @inject IHtmlLocalizer<PaymentResource> Localizer
    @section scripts {
        <abp-script-bundle>
            <abp-script src="/client-proxies/payment-proxy.js" />
            <abp-script src="/Pages/Payment/gateway-selection.js" />
        </abp-script-bundle>
    }
    <div class="container">
        <div class="row">
            <div class="col-md-7 mx-auto">
                <div class="card">
                    <div class="card-header">
                        <h4 class="m-0 text-center">Select a Payment Gateway</h4>
                    </div>
                    <div class="card-body">
                        <form id="frmGatewaySelection" method="post" action="">
                            @Html.AntiForgeryToken()
                            <div class="row">
                                @for (int gatewayIndex = 0; gatewayIndex < Model.Gateways.Count; gatewayIndex++)
                                {
                                    var gateway = Model.Gateways[gatewayIndex];
    
                                    <div class="col">
                                        <div class="form-check">
                                            <input type="hidden" id="@($"{@gateway.Name}PrePaymentUrl")" value="@(Model.UrlHelper.ReplaceTenantPlaceholder(gateway.PrePaymentUrl))?paymentRequestId=@Model.PaymentRequestId"/>
                                            <input type="radio" name="gateway" class="form-check-input" checked="@(gatewayIndex == 0)" value="@gateway.Name" id="@gateway.Name">
                                            <label for="@gateway.Name" class="form-check-label">
                                                <strong>@Localizer["PayWithGateway", Localizer[gateway.Name]]</strong>
                                                @if (gateway.Recommended)
                                                {
                                                    <small class="text-muted">(Recommended)</small>
                                                }
                                            </label>
                                            @if (gateway.ExtraInfos.Any())
                                            {
                                                <div class="payment-info-boxes">
                                                    @foreach (var extraInfo in gateway.ExtraInfos)
                                                    {
                                                        <p class="mt-1 mb-0 text-muted">
                                                            <small class="d-block"><i class="fa fa-info-circle"></i> @Html.Raw(extraInfo)</small>
                                                        </p>
                                                    }
                                                </div>
                                            }
                                        </div>
                                    </div>
                                    <input type="hidden" name="paymentRequestId" value="@Model.PaymentRequestId" />
                                }
                            </div>
                        </form>
                    </div>
                    <div class="card-body">
                        <div class="d-grid gap-2">
                            <a id="btnSubmit" href="#" class="btn btn btn-success @Model.CheckoutButtonStyle">Continue to Checkout <i class="fas fa-arrow-right"></i></a>
                        </div>
                        <p class="mt-2 mb-1 text-muted text-center">
                            <small>
                                <i class="fa fa-info-circle"></i> Next, you will be redirected to the selected payment gateway's website for the transaction
                            </small>
                        </p>
                    </div>
                </div>
            </div>
        </div>
    </div>
    
    
    [ExposeServices(typeof(GatewaySelectionModel))]
    public class MyGatewaySelectionModel : GatewaySelectionModel
    {
        private readonly IOptions<PaymentWebOptions> _paymentWebOptions;
        private readonly IGatewayAppService _gatewayAppService;
        private readonly IPaymentRequestAppService _paymentRequestAppService;
        public UrlHelper UrlHelper { get; set; }
       
        public MyGatewaySelectionModel(
            IPaymentRequestAppService paymentRequestAppService,
            IOptions<PaymentWebOptions> paymentWebOptions, 
            IGatewayAppService gatewayAppService) : base(paymentRequestAppService, paymentWebOptions, gatewayAppService)
        {
            _paymentWebOptions = paymentWebOptions;
            _gatewayAppService = gatewayAppService;
            _paymentRequestAppService = paymentRequestAppService;
        }
    
        public override async Task<IActionResult> OnPostAsync()
        {
            CheckoutButtonStyle = _paymentWebOptions.Value.GatewaySelectionCheckoutButtonStyle;
    
            var paymentRequest = await _paymentRequestAppService.GetAsync(PaymentRequestId);
    
            List<GatewayDto> gatewaysDtos;
    
            if (paymentRequest.Products.Any(a => a.PaymentType == PaymentType.Subscription))
            {
                gatewaysDtos = await _gatewayAppService.GetSubscriptionSupportedGatewaysAsync();
            }
            else
            {
                gatewaysDtos = await _gatewayAppService.GetGatewayConfigurationAsync();
            }
    
            Gateways = _paymentWebOptions.Value.Gateways
                .Where(x => gatewaysDtos.Any(a => a.Name == x.Key))
                .Select(x => x.Value)
                .ToList();
    
            if (!Gateways.Any())
            {
                throw new ApplicationException("No payment gateway configured!");
            }
    
            if (Gateways.Count == 1)
            {
                var gateway = Gateways.First();
                return LocalRedirectPreserveMethod(UrlHelper.ReplaceTenantPlaceholder((gateway.PrePaymentUrl) + "?paymentRequestId=" + PaymentRequestId));
            }
    
            return Page();
        }
    }
    
    [ExposeServices(typeof(PostPaymentModel))]
    public class MyPostPaymentModel : PostPaymentModel
    {
        public UrlHelper UrlHelper { get; set; }
    
        private IOptions<PaymentWebOptions> _paymentWebOptions;
        public MyPostPaymentModel(IPaymentRequestAppService paymentRequestAppService, IOptions<PaymentWebOptions> paymentWebOptions) : base(paymentRequestAppService, paymentWebOptions)
        {
            _paymentWebOptions = paymentWebOptions;
        }
        
        public override async Task<IActionResult> OnGetAsync()
        {
            if (SessionId.IsNullOrWhiteSpace())
            {
                return BadRequest();
            }
    
            var paymentRequest = await PaymentRequestAppService.CompleteAsync(
                StripeConsts.GatewayName,
                new()
                {
                    { StripeConsts.ParameterNames.SessionId, SessionId },
                });
    
            if (!_paymentWebOptions.Value.CallbackUrl.IsNullOrWhiteSpace())
            {
                var callbackUrl = UrlHelper.ReplaceTenantPlaceholder(_paymentWebOptions.Value.CallbackUrl + "?paymentRequestId=" + paymentRequest.Id);
    
                Response.Redirect(callbackUrl);
            }
    
            return Page();
        }
    }
    
    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    Sergei.Gorlovetsky created

    Hi liangshiwei,

    Thanks for response

    We had configured solutions you provided and override page and page model but we still face issue of tenant resolving

    please look below screen that we have tried locally and on productions

    Also when trying with debugging we found this with ReplaceTenantPlaceholder() method in UrlHelper class

    In this both url.contains condition goes false,so url will remain as is

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    liangshiwei created
    Fullstack Developer

    Hi,

    I'm checking

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    liangshiwei created
    Fullstack Developer

    Hi,

    Will it work if you try this?

    [ExposeServices(typeof(PrePaymentModel))]
    public class MyPrePaymentModel: PrePaymentModel
    {
        public override async Task OnPostAsync()
        {
            StartResult = await PaymentRequestAppService.StartAsync(StripeConsts.GatewayName, new PaymentRequestStartDto
            {
                ReturnUrl = UrlHelper.ReplaceTenantPlaceholder(PaymentWebOptions.RootUrl.RemovePostFix("/") + StripeConsts.PostPaymentUrl +
                             "?SessionId={CHECKOUT_SESSION_ID}"),
                CancelUrl = UrlHelper.ReplaceTenantPlaceholder(PaymentWebOptions.RootUrl),
    
                PaymentRequestId = PaymentRequestId
            });
    
            PublishableKey = StartResult.ExtraProperties[StripeConsts.ParameterNames.PublishableKey].ToString();
            SessionId = StartResult.ExtraProperties[StripeConsts.ParameterNames.SessionId].ToString();
        }
    }
    
    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    Sergei.Gorlovetsky created

    Hi,

    While integrating we face this issue

    The type 'StripeOptions' exists in both 'Volo.Payment.Stripe.Domain, Version=7.4.0.0, Culture=neutral, PublicKeyToken=null' and 'Volo.Payment.Stripe.Web, Version=7.4.0.0, Culture=neutral, PublicKeyToken=null'

    here our packages in identity server project

    can you take a look?

    Thanks in advance

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    liangshiwei created
    Fullstack Developer

    Hi,

    You can try:

    using StripeOptions = Volo.Payment.Stripe.StripeOptions;
    
    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    Sergei.Gorlovetsky created

    Hi,

    Yes i also tried this but same issue

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    liangshiwei created
    Fullstack Developer

    Hi,

    Sorry, I realize that it is impossible to replace the PrePaymentModel, you can try this:

    [ExposeServices(typeof(IPaymentRequestAppService))]
    public class MyPaymentRequestAppService : PaymentRequestAppService
    {
        private readonly UrlHelper _urlHelper;
        public MyPaymentRequestAppService(
            IPaymentRequestRepository paymentRequestRepository,
            PaymentGatewayResolver paymentMethodResolver, 
            IDistributedEventBus distributedEventBus, UrlHelper urlHelper) 
            : base(paymentRequestRepository, paymentMethodResolver, distributedEventBus)
        {
            _urlHelper = urlHelper;
        }
    
        public override Task<PaymentRequestStartResultDto> StartAsync(string gateway, PaymentRequestStartDto inputDto)
        {
            inputDto.CancelUrl = _urlHelper.ReplaceTenantPlaceholder(inputDto.CancelUrl);
            inputDto.ReturnUrl = _urlHelper.ReplaceTenantPlaceholder(inputDto.ReturnUrl);
            return base.StartAsync(gateway, inputDto);
        }
    }
    
    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    Sergei.Gorlovetsky created

    Hi,

    Thanks for your response & Sorry for late reply due to weekends,

    We have congifured this solution but no luck ,look below screen

    also face same error

    while running project locally there is no any break points trigger in MyPaymentRequestAppService.cs method

    Solutions configured is on right class library or something i missed?

    Please let me know

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    liangshiwei created
    Fullstack Developer

    Hi,

    Solutions configured is on right class library or something i missed?

    I'm not sure. could you use the suite to create a new project to reproduce the problem and share it with me? my email is shiwei.liang@volosoft.com I will check it asap.

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    Sergei.Gorlovetsky created

    Hi,

    Ok i have asked for it and we will let you soon for that

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    liangshiwei created
    Fullstack Developer

    ok

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    Sergei.Gorlovetsky created

    Hi, we are in process of giving access to the repository we are working on, so you can check and give the proper solution.

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    liangshiwei created
    Fullstack Developer

    Hi,

    May I ask which branch of the repo I should check?

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    Sergei.Gorlovetsky created

    Hi, Please check in branch "521 SocialExternalLogin", we have separate identity server and the changes you suggested are all in that web application only.

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    liangshiwei created
    Fullstack Developer

    Hi,

    Because the IdentityServer does not reference the Application project, that's why you can't hit the breakpoint.

    You need to move the MyPaymentRequestAppService to the IdentityServer project

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    Sergei.Gorlovetsky created

    Hi, Thanks for all your help, the issue has been fixed now.

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    liangshiwei created
    Fullstack Developer

    :)

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.8.0-preview. Updated on September 16, 2026, 14:50
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.