Hello ABP Support Team,
I’m encountering an issue when trying to install the Volo.Payment module via the ABP CLI.
Below is the command and output:
[12:32:03 INF] Press Enter to open your browser to login...
[12:32:52 INF] Successfully logged in as 'lexarius'
$ abp add-module Volo.Payment
[12:32:58 INF] You are running the second generation of the ABP CLI.
If you're interested in the legacy CLI, see https://abp.io/new-cli
System.Exception: Remote server returns '403-Forbidden'.
Message: PRO modules require a commercial license! Should be member of an organization with an active license.
Based on the error, it appears that Volo.Payment is a PRO module, and my account may not currently be associated with an organization that has an active commercial license. Could you help verify whether my account is correctly linked to the organization, or if there is anything else I need to configure?
I logged in with app-access@lexarius.com account
In addition, I’m planning to implement the following workflow:
A customer browses available courses/modules.
They add selected items to a shopping cart.
They proceed to checkout and make payment.
Upon successful payment:
- The customer is added to a join table (e.g.,
UserCourseorEnrollment). - This grants them access to the course(s) they purchased.
- The customer is added to a join table (e.g.,
Could you advise on the best practices to implement this in ABP?
33 Answer(s)
-
0
hi
Can you share the output of the
abp login-infocommand?Thanks.
-
0
- Solution
- About the 403 error and licensing
- Volo.Payment is a Commercial (PRO) module. Installing it with abp add-module requires your ABP account to be a member of an organization with an active Commercial license (Team or higher). Otherwise, the CLI returns 403 Forbidden with the “PRO modules require a commercial license” message.
- Verify your current CLI session and organization:
- Run: abp login-info
- Check that:
- Logged in: True
- Email matches app-access@lexarius.com
- OrganizationName and LicenseType show your company and an active license (Team/Business/Enterprise). If Organization is empty or LicenseType is not active, the CLI won’t allow adding Volo.Payment.
- If the login is stale or points to a wrong org, refresh: abp logout abp login app-access@lexarius.com abp login-info
- If you indeed have a Commercial license but your user is not linked to the org, ask your organization owner/admin to invite this email to the licensed organization from the ABP account portal. If you don’t have a Commercial license, you need to obtain one (Team or higher) to use Volo.Payment.
- If you still see licensing errors after confirming membership and license, clear local session and retry:
- abp logout
- Delete the ABP CLI auth cache folder on the machine (if any), then abp login again.
- Implementing cart, checkout, payment, and access granting in ABP
- Recommended building blocks:
- Domain entities: Course, Order, OrderItem, PaymentRecord (or Payment), Enrollment (UserCourse)
- Application services: CartAppService, OrderAppService, PaymentAppService, EnrollmentAppService
- Integration with Payment: Prefer ABP’s Payment Module (PRO) for Stripe/PayPal/etc. If you don’t/can’t use it, you can integrate Stripe SDK directly in your app service following ABP’s layered architecture and unit of work.
- High-level flow (with ABP Payment Module)
- Cart:
- Persist cart items per user (e.g., a Cart aggregate or transient client-side cart sent at checkout).
- Validate availability/prices via application service.
- Create Order (domain-driven):
- In an application service method, map cart items to an Order aggregate and OrderItems; calculate totals; save Order with status PendingPayment.
- Create Payment Request:
- Use the Payment Module to create a payment request associated with the Order ID and total amount; select the gateway (Stripe).
- Redirect the user to Stripe checkout (hosted) or collect payment method and confirm on the server, depending on gateway flow.
- Handle payment success callback/webhook:
- Implement the gateway callback/webhook endpoint. In the handler:
- Verify signature/intent status.
- Update PaymentRecord to Succeeded and Order to Paid in a single unit of work.
- Publish a domain event like OrderPaidEvent.
- Implement the gateway callback/webhook endpoint. In the handler:
- Grant access on success:
- Handle OrderPaidEvent (or subscribe to the PaymentSucceeded event if using the Payment Module) to create Enrollment rows per purchased Course: repository.InsertAsync(new Enrollment(userId, courseId));
- Optionally, assign permissions/roles or feature flags if access is permission-based, but a simple Enrollment join table is typical.
- Authorization:
- In course access queries/controllers, filter by current user’s enrollments to authorize content access.
- Cart:
- Example skeleton
- Domain event after payment success:
- Recommended building blocks:
public class OrderPaidEvent : EntityEventData<Order> { public OrderPaidEvent(Order entity) : base(entity) {} }- Event handler to grant access:public class OrderPaidEventHandler : ILocalEventHandler<OrderPaidEvent>, ITransientDependency { private readonly IRepository<Enrollment, Guid> _enrollmentRepo; public OrderPaidEventHandler(IRepository<Enrollment, Guid> enrollmentRepo) { _enrollmentRepo = enrollmentRepo; } public async Task HandleEventAsync(OrderPaidEvent eventData) { var order = eventData.Entity; foreach (var item in order.Items) { await _enrollmentRepo.InsertAsync(new Enrollment(order.UserId, item.CourseId), autoSave: true); } } }- Guarding course content:public class CourseAppService : ApplicationService { private readonly IRepository<Enrollment, Guid> _enrollmentRepo; private readonly IRepository<Course, Guid> _courseRepo; public async Task<CourseDto> GetAsync(Guid courseId) { var isEnrolled = await _enrollmentRepo.AnyAsync(e => e.UserId == CurrentUser.GetId() && e.CourseId == courseId); if (!isEnrolled) throw new AbpAuthorizationException("Not enrolled"); var course = await _courseRepo.GetAsync(courseId); return ObjectMapper.Map<Course, CourseDto>(course); } }- If you cannot use the PRO Payment Module:
- Keep the same domain flow (Order, PaymentRecord, events).
- Integrate Stripe Checkout/Payment Intents in your application service and controller. Persist PaymentRecord with external transaction IDs. On webhook, update Order and raise the same OrderPaidEvent to centralize the access granting logic.
- Multi-tenancy and consistency:
- Use ABP UnitOfWork in application services and event handlers.
- Use background jobs only if post-payment steps are long-running; otherwise handle synchronously inside the same UoW triggered by the webhook/event.
- Sources
- ABP Module System basics:
- https://abp.io/docs/latest/framework/architecture/modularity/basics
- ABP Payment Module (Commercial/PRO) overview:
- https://abp.io/docs/latest/modules/payment
- ABP new CLI reference page mentioned in your log:
- https://abp.io/new-cli
- ABP Commercial and PRO modules/licensing references (context on PRO modules and access):
- https://github.com/abpframework/abp
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
-
0
[maliming] said: hi
Can you share the output of the
abp login-infocommand?Thanks.
$ abp login-info [09:59:39 INF] You are running the second generation of the ABP CLI. If you're interested in the legacy CLI, see https://abp.io/new-cli 📌 ABP CLI 0.9.26 📌 A newer version of the ABP CLI is available: 2.0.2 🔼 dotnet tool update -g Volo.Abp.Studio.Cli [09:59:41 INF] Login info: Name: Lexarius Limited Surname: Username: lexarius Email Address: app-access@lexarius.com Organization: -
0
- Solution
Why the 403 happens
- Volo.Payment is a Commercial (PRO) module. The ABP CLI only allows installing PRO modules if your logged-in user belongs to an organization that has an active Commercial license.
- Your login-info shows Organization is empty, so the CLI treats you as not licensed for PRO modules.
Fix your account/organization linkage
- Ask your organization owner/admin to invite app-access@lexarius.com to the licensed organization from the ABP account portal. Once accepted, re-run:
- abp logout
- abp login app-access@lexarius.com
- abp login-info // Organization should now show your company name
- If you believe you already have a valid Commercial license but still see Organization empty, please contact ABP Support to link your user to the organization.
- Optionally update your CLI (you’re on 0.9.26, latest is 2.x):
- dotnet tool update -g Volo.Abp.Studio.Cli
- abp login-info
- Ask your organization owner/admin to invite app-access@lexarius.com to the licensed organization from the ABP account portal. Once accepted, re-run:
Implementing cart → checkout → payment → access granting
If you use the ABP Payment Module (Commercial):
- Create an Order aggregate (Order + OrderItems) from the cart and persist it with status PendingPayment.
- Create a payment request via the Payment Module for the Order total and gateway = Stripe.
- Redirect to Stripe Checkout or confirm Payment Intent as required by the gateway.
- Handle Stripe webhook/callback to mark payment Successful and set Order to Paid within a Unit of Work.
- On success, create Enrollment (UserId, CourseId) rows for each purchased item, or publish a domain event (e.g., OrderPaidEvent) and handle it to insert enrollments.
- Enforce access by checking Enrollment before serving course content.
If you can’t use the Commercial module:
- Keep the same domain flow (Order, PaymentRecord, events) and integrate Stripe SDK directly. Use a secured webhook endpoint, verify signatures, then update Order and create Enrollment in a single UoW.
Example event handler to grant access after payment:
public class OrderPaidEventHandler : ILocalEventHandler<OrderPaidEvent>, ITransientDependency { private readonly IRepository<Enrollment, Guid> _enrollmentRepo; public OrderPaidEventHandler(IRepository<Enrollment, Guid> enrollmentRepo) { _enrollmentRepo = enrollmentRepo; } public async Task HandleEventAsync(OrderPaidEvent eventData) { var order = eventData.Entity; foreach (var item in order.Items) { await _enrollmentRepo.InsertAsync(new Enrollment(order.UserId, item.CourseId), autoSave: true); } } }- Sources
- ABP CLI login-info command:
- https://abp.io/docs/latest/cli/
- ABP Module System:
- https://abp.io/docs/latest/framework/architecture/modularity/basics
- ABP Payment Module (Commercial):
- https://docs.abp.io/en/commercial/latest/modules/payment
- ABP new CLI page:
- https://abp.io/new-cli
- For licensing or organization membership issues, please contact:
- https://abp.io/contact
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
-
0
hi
There is no organization info for your current user.
Can you check and share the
Lexarius Limitedaccount info from https://abp.io/my-organizationsLexarius Limitedshould be in the Developers section.Login info: Name: Lexarius Limited Surname: Username: lexarius Email Address: app-access@lexarius.com Organization:Thanks.
-
0
-
0
hi
Can you try logging in to the
Bryan-EDVaccount in the CLI?Thanks.
-
0
i think Bryan-EDV was renamed to app-access@lexarius.com aka lexarius
-
0
-
0
-
0
hi
Logging out and logging in again from https://abp.io may help.
Can you try that?
Thanks.
-
0
-
0
-
0
Sure, thanks.
For the other part of the qn,
In addition, I’m planning to implement the following workflow:
- A customer browses available courses/modules.
- They add selected items to a shopping cart.
- They proceed to checkout and make payment.
- Upon successful payment:
- The customer is added to a join table (e.g., UserCourse or Enrollment).
- This grants them access to the course(s) they purchased.
Could you advise on the best practices to implement this in ABP?
-
0
hi
You can use the repository and app service to query for and display courses on the UI page.
And then add a new entity to store the shopping cart items for the current user.
Use the ABP payment module for the payment feature(
download the source code of the payment module to check the demo apps).After that, you can get the courses that users have purchased.
The features provided by ABP, such as entities, repository, application services, and UI, are more than sufficient
Thanks.
-
0
Hi Maliming,
Take a simplified flow:
- User adds items to cart
- Upon checkout, create one payment request with _paymentRequestAppService.CreateAsync
- the payment request would have a list of products which correspond to the items in the cart
- on each product, i add extra properties on the details of the product. e.g. course id, target user id
- I listen to the Volo.Payment.PaymentRequestCompleted event
- Handle event by adding user
paymentRequest.Products[n].ExtraProperties.UserIdto the relevant coursespaymentRequest.Products[n].ExtraProperties.CourseId
Is this flow and my understanding correct?
Could you also advise what steps are needed if we are using ABP with Angular?
Many thanks
-
0
hi
Your flow is no problem. The public Angular UI for the payment module will be available in the 10.1 milestone.
https://abp.io/docs/latest/modules/payment#user-interface
I recommend downloading the payment module's source code to see how it works.
Thanks
-
0
We are using angular + abp setup
Would there be any issues if we were to try using the module leveraging razor pages? or better to wait till 10.1 before implementing the FE screens? thanks
-
0
hi
Razor page will be no problem. You can use it to test it now
Thanks.
-
0
Hi Maliming,
In the event that a payment fails, is there an event for that? I see that in the docs there is only an event for Volo.Payment.PaymentRequestCompleted
-
0
hi
I think we can't know if the user has made the payment.
Thanks.
-
0
Hi,
In that case what is an event we can listen to to trigger enrollment of the user in his course he purchased?
My initial flow assumes will enrol him to the course when we KNOW the payment is successful
And on the flipside we will NOT enrol him once we know that payment failed.
Many thanks for clarification
-
0
hi
You can listen to the
PaymentRequestCompletedevent, then add your course business.Add a status property and set it to valid only when the payment is successful.
If the status is invalid, the user can't see the course.
Thanks.
-
0
Hi Maliming
I'm trying to init a payment request and redirect the user to the URL to make payment
http://localhost:44346/Payment/GatewaySelection?paymentRequestId=3a1e4773-46cd-efff-049d-874703628099
testing locally however this URL is returning 404. Am i doing something wrong? Many thanks
-
0
hi
Can you check the logs?
Or you can set a breakpoint to see why the page returns a 404.
Thanks






