Skip to Content Skip to Menu

Programmatically generate purchase item

  • sstoks
  • sstoks
  • ONLINE
  • Posts: 4
  • Thanks: 0
  • Karma: 0
5 days 23 hours ago #343037 by sstoks
Hello,

For context: I "inherited" a web site for which I am now the webmaster.  This is a Joomla site which has been handled by various people for many years and by now it's frankly a bit of a mess.  I am not a Joomla expert and this is the first time I'm getting acquainted by it.  I volunteered because I'm an (embedded) software engineer and figured "how hard can it be".  I feel much more at home in a code editor than in a visual environment.
The site is for a society with <1000 (paying) members, and it uses CBSubs to manage that.  The society also (occasionally) sells items via a webshop (HikaShop) and organizes yearly events (with RSEvents) for which the site should handle the ticketing/registration process.
I figured out that it uses a "hybrid" approach in which the event tickets are actually "CBSub plans" behind the scenes.  The previous maintainers wrote a custom plugin which basically "intercepts" the ticketing form once it's submitted, replaces the items by CB plans, and forwards the user to the CBSub payment page.  They did this by not only doing string-based text replacements, but even edited the core CB files to get this to work.  I found this out because googling for a solution, I ended up on these very forums, with you guys (rightfully) saying "don't do that".
Of course, after I updated the ancient versions of the plugins, everything broke.

Since I am under time pressure (the event registration for the upcoming event must be enabled in the coming days), I think I need a solution to programmatically generate CBSub "shopping cart contents" and pass it on to the correct payment processing system.  The reason I want to do this, is that the other board members of the society are used to the (nice) payment overview system that CBSub offers, and they can quickly see who registered and who paid (including via various offline payment systems), and especially because then the "invoice" system would be taken care of.

TL;DR: In a "form submit event" elsewhere on my site, I can run arbitrary PHP code.  I can parse the form data myself, and would like to do something like

$cbsub = somehow_find_the_entry_point_to_cbsub_functionality();
$cart = cbsub.generate_purchase();
$cart.add_line("Ticket for the Workshop", $workshop_price);
$cart.add_line("Extra cookie with the coffee", $cookie_price);
$cbsub.pretend_the_user_just_clicked_checkout($cbuser, $cart);

(excuse my syntax, I'm not a native PHP speaker)

after which the user would be taken to a (preconfigured) payment page where they can select between PayPal and offline bank transfer, an invoice is generated (I'm also willing to provide the text for that programmatically myself by now), payment is taken care of, an email is sent out with "User John Doe just registered for the workshop and wants [1] extra cookie, offline payment is still pending" or something like that, and a line is added with this info to the CBSub "Payments Center" overview.

My initial gut feeling was that this is "misuse" of a system designed for paid subscriptions (in fact, the current implementation indeed handles this one-time ticket purchase as some special yearly subscription plan) and I should actually be asking this on a HikaShop forum, but the payment processors are better maintained on the CB part of the site, the generated overviews are much nicer, the invoices look better, support on these forums seems more active, and we can probably abuse a "merchandise" functionality for this use case. 

Kind regards,
Sander

Please Log in or Create an account to join the conversation.

  • krileon
  • krileon
  • ONLINE
  • Posts: 50068
  • Thanks: 8568
  • Karma: 1467
5 days 10 hours ago #343038 by krileon
Replied by krileon on topic Programmatically generate purchase item
They might be using subscriptions in order to allow CBSubs to grant access to parts of the site. As for modifying a plans price it maybe possible to just use CBSubs Options for that depending on whether "add_line" are static or if you truly need to generate a payment basket with basically unknown line items.

If you're comfortable with PHP it maybe entirely possible to generate a basket, add line items to it, and then send them to that basket to pay it. All without having to do string replacements or anything like that. For the custom line items you'd either need to use donation plans, since they are designed for variable pricing, or adjusting CBSubs Options programmatically. Either should work fine.

I recommend taking a look at CBSubs Options first to see if just statically setting up a plan could work and if you're not sure that'll do that job for you I can provide a code example of generating a custom basket.


Kyle (Krileon)
Community Builder Team Member
Before posting on forums: Read FAQ thoroughly + Read our Documentation + Search the forums
CB links: Documentation - Localization - CB Quickstart - CB Paid Subscriptions - Add-Ons - Forge
--
If you are a Professional, Developer, or CB Paid Subscriptions subscriber and have a support issue please always post in your respective support forums for best results!
--
If I've missed your support post with a delay of 3 days or greater and are a Professional, Developer, or CBSubs subscriber please send me a private message with your thread and will reply when possible!
--
Please note I am available Monday - Friday from 8:00 AM CST to 4:00 PM CST. I am away on weekends (Saturday and Sunday) and if I've missed your post on or before a weekend after business hours please wait for the next following business day (Monday) and will get to your issue as soon as possible, thank you.
--
My role here is to provide guidance and assistance. I cannot provide custom code for each custom requirement. Please do not inquire me about custom development.

Please Log in or Create an account to join the conversation.

  • krileon
  • krileon
  • ONLINE
  • Posts: 50068
  • Thanks: 8568
  • Karma: 1467
5 days 6 hours ago - 1 day 10 hours ago #343048 by krileon
Replied by krileon on topic Programmatically generate purchase item
Ok, it took awhile, but was able to put together an example of generating a basket from PHP with modified donation plan price. Donation plans are the only way to go here for variable price line items since they're designed internally to accept variable prices. Other plans would fail since they will validate their their price doesn't match.

With the example that will follow you'll want to have the following setup.

1. Subscription plan with the duration and base price you want. This subscription will be used to grant access throughout the site however you need. IF this subscription plan is going to be reused for additional purchases you'll need to set "Multiple subscriptions per user" to "Multiple subscriptions per user allowed" under the Workflows tab. That is important otherwise this process won't work if that subscription plan is going to be reused for other purchases.
2. A donation plan with "Are donation amounts given ?" set to "No suggested donation amount, free field" for each line item you intend to have available. These will be used to add those line items to purchases.

It's worth noting you do NOT have to use Step 1 if you don't actually need subscriptions. You can just use donation plans if you want. Now onto the code.

Code:
if ( ( ! file_exists( JPATH_SITE . '/libraries/CBLib/CBLib/Core/CBLib.php' ) ) || ( ! file_exists( JPATH_ADMINISTRATOR . '/components/com_comprofiler/plugin.foundation.php' ) ) ) {     return; } global $_CB_framework, $_PLUGINS; include_once( JPATH_ADMINISTRATOR . '/components/com_comprofiler/plugin.foundation.php' ); cbimport( 'cb.html' ); cbimport( 'language.front' ); $_PLUGINS->loadPluginGroup( 'user' ); $planIds = [ 43, 11 ]; // This is just an array of plan ids you want to add to the basket $lineItems = [ 11 => 10 ]; // This is a key value pair of PLAN_ID => PRICE to alter the donation price programmatically // The following will call internal API and build up the plan selection process and apply it to the basket for basket generation $user = CBuser::getMyUserDataInstance(); $now = $_CB_framework->now(); $input = \CBLib\Application\Application::Input(); $_GET['cbpplanN'][0]['selected'] = $planIds; $input->set( 'cbpplanN.0.selected', $planIds ); foreach ( $lineItems as $planId => $price ) {     $_GET['cbpplanN'][0]['donate']['plan'.$planId]['donval'] = $price;     $input->set( 'cbpplanN.0.donate.plan' . $planId . '.donval', $price ); } $gui = new cbpaidControllerUI(); $plans = $gui->checkChosenUpgradePlans( $planIds, $user, $now ); if ( ! is_array( $plans ) ) {     return; // If no valid plans could be added it will abort here so maybe you'd want to do some kind of error handling? } $paymentBasket = cbpaidControllerOrder::createSubscriptionsAndPayment( $user, $plans, $_GET ); cbRedirect( $paymentBasket->getShowBasketUrl( false ) ); // This redirects to the basket to accept payment


The first part is loading in CBs API externally as I'm guessing this is being done outside of CB. Next you need to set your arrays for your plans. I've commented them so you can adjust that section to whatever you need. All it requires is plan ids and plan prices for additional plans being added to the basket. So in this example it's purchasing plans 43 and 11. It then sets the price of the donation plan (11) to $10.

That's it. It should redirect to the basket and be ready to accept payment.

I should still warn that CBSubs was never really intended to be used this way. However it should reliably work for the foreseeable future without needing str replacements or other modifications like that. If you need to modify the line item text in the basket itself that's also doable so please let me know if you also need that.


Kyle (Krileon)
Community Builder Team Member
Before posting on forums: Read FAQ thoroughly + Read our Documentation + Search the forums
CB links: Documentation - Localization - CB Quickstart - CB Paid Subscriptions - Add-Ons - Forge
--
If you are a Professional, Developer, or CB Paid Subscriptions subscriber and have a support issue please always post in your respective support forums for best results!
--
If I've missed your support post with a delay of 3 days or greater and are a Professional, Developer, or CBSubs subscriber please send me a private message with your thread and will reply when possible!
--
Please note I am available Monday - Friday from 8:00 AM CST to 4:00 PM CST. I am away on weekends (Saturday and Sunday) and if I've missed your post on or before a weekend after business hours please wait for the next following business day (Monday) and will get to your issue as soon as possible, thank you.
--
My role here is to provide guidance and assistance. I cannot provide custom code for each custom requirement. Please do not inquire me about custom development.
Last edit: 1 day 10 hours ago by krileon.

Please Log in or Create an account to join the conversation.

  • sstoks
  • sstoks
  • ONLINE
  • Posts: 4
  • Thanks: 0
  • Karma: 0
2 days 3 hours ago - 2 days 2 hours ago #343068 by sstoks
Replied by sstoks on topic Programmatically generate purchase item
Hi Kyle, thanks for the code! I tried it out, and seem to be getting somewhere.
I had to remove the "$_PLUGINS->loadPluginGroup( 'user' );" line, because I was getting a nullptr dereference. But the rest of the code seemed to work so far.

I skipped step 1 because the "workshop tickets" should be available both for members (who will have a subscription) and non-members (who only wish to attend the workshop but don't wish to become a member). So I went to step 2 and generated a new Subscription Plan of the "donation" type, "Not exclusive", called "Workshop".

When running through the code, the result of "$plans = $gui->checkChosenUpgradePlans( $planIds, $user, $now );" is not an array but an error message:

"Chosen plans combination is not allowed (you must choose coherent plans selection, e.g. parent subscriptions to a plan must be active)"

I assume this is not necessarily a problem with the code but rather with how I have configured the plan.  I notice that plans have a gazillion of options, but I left most of them to their default.  Now I see "Parent plan" listed among the options, and I left it to "Top" (the default).  In the menu, I see all the other plans listed, but I don't see how it makes sense to select any of them (a "donation" should be possible without any other plans being active, right?)

I hope that adding this "template plan" to the basket will also let me programmatically change its description (same for the invoice) like you mentioned in your reply, so that I can indeed keep using this one plan as a kind of "API into the payment system".  I fully understand that this would be a "brittle API" but at least it's better than str-replace, and if you ever decide to make a "proper" API for this, I'll gladly switch over to it :-)

(EDIT: I googled for this exact error message and found this thread:  www.joomlapolis.com/forum/professional-members-support/225222-solved-chosen-plans-combination-is-not-allowed-you-must-choose-coherent-plans-selection-e-g-parent-subscriptions-to-a-plan-must-be-active - but I tried the suggestion there of switching the "Propose spontaneously..." to "Yes", but that didn't help.)

(EDIT 2: I tried to fiddle around with the various options in the plan, and am now at a state that "$plans = $gui->checkChosenUpgradePlans( $planIds, $user, $now);" doesn't return but rather errors out with "Call to a member function _getReqParam() on null")...
Last edit: 2 days 2 hours ago by sstoks. Reason: different error

Please Log in or Create an account to join the conversation.

  • krileon
  • krileon
  • ONLINE
  • Posts: 50068
  • Thanks: 8568
  • Karma: 1467
1 day 10 hours ago - 1 day 9 hours ago #343073 by krileon
Replied by krileon on topic Programmatically generate purchase item

I had to remove the "$_PLUGINS->loadPluginGroup( 'user' );" line, because I was getting a nullptr dereference. But the rest of the code seemed to work so far.

That's necessary otherwise CBSubs won't be loaded. Add global $_PLUGINS; above that. Have edited my post with that fixed as that was added after testing my implementation since it wasn't needed when testing in CB Auto Actions.

I assume this is not necessarily a problem with the code but rather with how I have configured the plan.

Correct, your plans can't be combined. If you can't purchase the subscription plan and the donation plans normally from frontend then it won't work. The idea is you first configure them so they purchase fine from frontend then just hide them by configuring them to only display if accessed by URL. So I would do this first before worrying about the code. I'll see if I can write some code that bypasses this, but I'm not sure what'll happen if you do since that's not really a case CBSubs is designed for.

I hope that adding this "template plan" to the basket will also let me programmatically change its description (same for the invoice) like you mentioned in your reply

Ok, will provide an example with the line item text overridden as well.

if you ever decide to make a "proper" API for this, I'll gladly switch over to it

Maybe one day, but it's never how we intended CBSubs to be used as it's a subscription manager not a storefront.


Kyle (Krileon)
Community Builder Team Member
Before posting on forums: Read FAQ thoroughly + Read our Documentation + Search the forums
CB links: Documentation - Localization - CB Quickstart - CB Paid Subscriptions - Add-Ons - Forge
--
If you are a Professional, Developer, or CB Paid Subscriptions subscriber and have a support issue please always post in your respective support forums for best results!
--
If I've missed your support post with a delay of 3 days or greater and are a Professional, Developer, or CBSubs subscriber please send me a private message with your thread and will reply when possible!
--
Please note I am available Monday - Friday from 8:00 AM CST to 4:00 PM CST. I am away on weekends (Saturday and Sunday) and if I've missed your post on or before a weekend after business hours please wait for the next following business day (Monday) and will get to your issue as soon as possible, thank you.
--
My role here is to provide guidance and assistance. I cannot provide custom code for each custom requirement. Please do not inquire me about custom development.
Last edit: 1 day 9 hours ago by krileon.

Please Log in or Create an account to join the conversation.

  • krileon
  • krileon
  • ONLINE
  • Posts: 50068
  • Thanks: 8568
  • Karma: 1467
5 hours 17 minutes ago #343093 by krileon
Replied by krileon on topic Programmatically generate purchase item
Did you have time to take a look at CBSubs Options to see if it might be a fit for your current situation? If not can you provide some examples of what a payment might look like for your situation? Are you needing basically to treat CBSubs as a means of paying a cart from another extension entirely so that each line item could be wildly different? There's difficulty in making CBSubs accept random line items since it expects a line item to be a plan of some kind, but am exploring using totalizers (what tax and promotions plugin use) to modify pricing, but there's no guarantee this will work as desired either. CBSubs just isn't designed to be a generic shopping cart so there's going to have to be some compromise here.


Kyle (Krileon)
Community Builder Team Member
Before posting on forums: Read FAQ thoroughly + Read our Documentation + Search the forums
CB links: Documentation - Localization - CB Quickstart - CB Paid Subscriptions - Add-Ons - Forge
--
If you are a Professional, Developer, or CB Paid Subscriptions subscriber and have a support issue please always post in your respective support forums for best results!
--
If I've missed your support post with a delay of 3 days or greater and are a Professional, Developer, or CBSubs subscriber please send me a private message with your thread and will reply when possible!
--
Please note I am available Monday - Friday from 8:00 AM CST to 4:00 PM CST. I am away on weekends (Saturday and Sunday) and if I've missed your post on or before a weekend after business hours please wait for the next following business day (Monday) and will get to your issue as soon as possible, thank you.
--
My role here is to provide guidance and assistance. I cannot provide custom code for each custom requirement. Please do not inquire me about custom development.

Please Log in or Create an account to join the conversation.

Moderators: beatnantkrileon
Powered by Kunena Forum