Cardinal Cruise Authentication is no longer available. There is no replacement.
If Visa protect is in fact a 3DS solution it would likely need a new integration written for it.
Authorize.net has no desire to support 3DS.
https://support.authorize.net/knowledgebase/Knowledgearticle/?code=00000...
They recommend using another payment gateway if you need 3DS support...
Apparently lcobucci/clock was updated to support PHP 8.3 and 8.4 which made this module compatible with Drupal 11.
I have double checked this on a fresh Drupal 11 project.
Still would be a good idea to upgrade to a newer version eventually.
All validation tests are green now.
- Tested module on Drupal 10 and 11.
- Tested Google Maps provider.
- Tested Mapbox Provider.
- France provider has no code changes so did not test.
- Did not test PostCH because could not figure out how to get a test account.
If someone is able to test PostCH that would be appreciated.
Otherwise this is ready for review and commit.
rhovland → created an issue.
rhovland → created an issue.
A token can be easily harvested from a public page where nobody needs to be logged in and then used to authorize automated requests to the json endpoint. How would we handle rotating that token? What happens when it's rotated between user requests?
This doesn't seem to be an issue anymore, at least with the google maps provider. Can you provide more information about what provider you are using?
I'm guessing this was related to this issue which is no longer applicable. 🐛 Google Maps integration incorrectly prepopulates address fields Closed: outdated
Please reopen if this is still a problem on the latest version of the module.
The google maps provider was rewritten and I'm not seeing any of these problems with it. Closing as outdated. Please reopen if you can reproduce any of these issues in the latest release.
The new google maps provider is much simpler and works with the Places API so this is now no longer relevant.
This is no longer applicable to the google maps plugin as it filters by country now. As there was no input provided if this effects other providers going to close this. Please reopen if this affects you.
The policy in question is called security advisory coverage meaning projects are being opted into the advisory process which includes a private issue section for potential security related issues.
It is not a guarantee of security of a module. It means a module has opted into using a private queue for security issues.
The only requirements to opt a module in is that it is in a finished state (stable).
The only requirements for a user to opt a module they maintain into security advisory coverage is them demonstrating they know how to write secure code. If they don't know how to do this then security issues being sent to a private queue only delays disclosure as there is nobody there to fix it (don't know how to write secure code).
If a project opted into security advisory coverage doesn't fix reported security issues then it is removed from security advisory coverage and the issues are made public.
If a maintainer has failed to do this presumably they would have the ability to opt-in revoked as well if warranted.
If you feel you were unfairly removed from being able to opt-in modules you maintain into security advisory coverage (private security issue queue) then explain why. Pointing fingers at others does not contribute anything but noise for drupal ecosystem maintainers. Unreported security issues in someone's module are not grounds for removal. Failure to fix REPORTED issues in a timely manner are.
rhovland → created an issue.
Cleaned up the language further
Issue is ready for review. Test failure is from the main branch and not related to these changes.
Before:
After:
Oh it looks like when the user is anonymous it already prompts for the name. If the user is logged in it uses "my" instead. That seems silly. At least it could use the username. But I think it's better to have that entry box be there, anon or not, as the username isn't always the person's name or a useful identifier for the recipient.
rhovland → created an issue.
rhovland → created an issue.
rhovland → created an issue.
@goz You got this to successfully write order adjustments? Do you have commerce_api installed?
Ok so after combing through things, we were using #72 🐛 [PP-1] Order's Adjustment can't be normalized and serialized Postponed plus the core Any normalizer patch and it was working.
The issue seems to be the final commit never implemented a denormalizer so that PATCH operations can work. The patch we were using had one. Kinda.
diff --git a/modules/order/src/Plugin/Field/FieldType/AdjustmentItem.php b/modules/order/src/Plugin/Field/FieldType/AdjustmentItem.php
index 81ae6c90..cbc96972 100644
--- a/modules/order/src/Plugin/Field/FieldType/AdjustmentItem.php
+++ b/modules/order/src/Plugin/Field/FieldType/AdjustmentItem.php
@@ -3,6 +3,7 @@
namespace Drupal\commerce_order\Plugin\Field\FieldType;
use Drupal\commerce_order\Adjustment;
+use Drupal\commerce_price\Price;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\TypedData\DataDefinition;
@@ -48,6 +49,11 @@ class AdjustmentItem extends FieldItemBase {
// The property definition causes the adjustment to be in 'value' key.
$values = reset($values);
}
+ // Used for denormalization.
+ if (is_array($values)) {
+ $values['amount'] = new Price($values['amount']['number'], $values['amount']['currency_code']);
+ $values = new Adjustment($values);
+ }
if (!$values instanceof Adjustment) {
$values = NULL;
}
So as committed this lets you read adjustments via API but not write them.
If I try to send a patch request to https://example.com/jsonapi/commerce_order/default/ab42ab1c-b21f-4da2-9e...
With JSON
{
"data":{
"type": "commerce_order--default",
"id": "ab42ab1c-b21f-4da2-9e9b-82213e622e59",
"attributes": {
"adjustments": [
{
"type": "shipping",
"label": "Shipping",
"amount": {
"number": "126.940000",
"currency_code": "USD"
},
"percentage": null,
"source_id": "209676",
"included": false,
"locked": false
}
]
}
}
}
I get the response
{
"jsonapi": {
"version": "1.0",
"meta": {
"links": {
"self": {
"href": "http://jsonapi.org/format/1.0/"
}
}
}
},
"data": {
"type": "commerce_order--default",
"id": "ab42ab1c-b21f-4da2-9e9b-82213e622e59",
"links": {
"self": {
"href": "https://example.com/jsonapi/commerce_order/default/ab42ab1c-b21f-4da2-9e9b-82213e622e59"
}
},
"attributes": {
"drupal_internal__order_id": 289391,
"order_number": "289391",
"version": 15,
"mail": "redacted@example.com",
"ip_address": "208.56.233.218",
"adjustments": [],
"total_price": {
"number": "226.22",
"currency_code": "USD",
"formatted": "$226.22"
},
"total_paid": {
"number": "253.160000",
"currency_code": "USD",
"formatted": "$253.16"
},
"balance": {
"number": "-26.94",
"currency_code": "USD",
"formatted": "-$26.94"
},
"state": "shipped",
"data": {
"paid_event_dispatched": true
},
"locked": false,
"created": "2024-12-16T21:19:32+00:00",
"changed": "2025-05-16T20:19:03+00:00",
"placed": "2024-12-16T21:28:24+00:00",
"completed": null,
"customer_comments": null,
"cart": false,
"checkout_step": null,
"field_customer_comments": null,
"field_customer_motorcycles": [],
"field_newsletter": false
},
"relationships": {
"commerce_order_type": {
"data": null,
"links": {
"self": {
"href": "https://example.com/jsonapi/commerce_order/default/ab42ab1c-b21f-4da2-9e9b-82213e622e59/relationships/commerce_order_type"
}
}
},
"store_id": {
"data": null,
"links": {
"self": {
"href": "https://example.com/jsonapi/commerce_order/default/ab42ab1c-b21f-4da2-9e9b-82213e622e59/relationships/store_id"
}
}
},
"uid": {
"data": {
"type": "user--user",
"id": "e369285a-16a1-4817-baa5-b4848443a28a",
"meta": {
"drupal_internal__target_id": 29634
}
},
"links": {
"related": {
"href": "https://example.com/jsonapi/commerce_order/default/ab42ab1c-b21f-4da2-9e9b-82213e622e59/uid"
},
"self": {
"href": "https://example.com/jsonapi/commerce_order/default/ab42ab1c-b21f-4da2-9e9b-82213e622e59/relationships/uid"
}
}
},
"billing_profile": {
"data": {
"type": "profile--customer",
"id": "2cfa79d0-3d74-4c99-bff2-3a26ee115a21",
"meta": {
"target_revision_id": 363841,
"drupal_internal__target_id": 364046
}
},
"links": {
"related": {
"href": "https://example.com/jsonapi/commerce_order/default/ab42ab1c-b21f-4da2-9e9b-82213e622e59/billing_profile"
},
"self": {
"href": "https://example.com/jsonapi/commerce_order/default/ab42ab1c-b21f-4da2-9e9b-82213e622e59/relationships/billing_profile"
}
}
},
"order_items": {
"data": [
{
"type": "commerce_order_item--parts",
"id": "55aaae86-e1dc-4c22-943e-7503deef65b7",
"meta": {
"drupal_internal__target_id": 1073893
}
}
],
"links": {
"related": {
"href": "https://example.com/jsonapi/commerce_order/default/ab42ab1c-b21f-4da2-9e9b-82213e622e59/order_items"
},
"self": {
"href": "https://example.com/jsonapi/commerce_order/default/ab42ab1c-b21f-4da2-9e9b-82213e622e59/relationships/order_items"
}
}
},
"checkout_flow": {
"data": null,
"links": {
"self": {
"href": "https://example.com/jsonapi/commerce_order/default/ab42ab1c-b21f-4da2-9e9b-82213e622e59/relationships/checkout_flow"
}
}
},
"commerce_giftcards": {
"data": [],
"links": {
"self": {
"href": "https://example.com/jsonapi/commerce_order/default/ab42ab1c-b21f-4da2-9e9b-82213e622e59/relationships/commerce_giftcards"
}
}
},
"payment_gateway": {
"data": null,
"links": {
"self": {
"href": "https://example.com/jsonapi/commerce_order/default/ab42ab1c-b21f-4da2-9e9b-82213e622e59/relationships/payment_gateway"
}
}
},
"payment_method": {
"data": null,
"links": {
"self": {
"href": "https://example.com/jsonapi/commerce_order/default/ab42ab1c-b21f-4da2-9e9b-82213e622e59/relationships/payment_method"
}
}
},
"coupons": {
"data": [],
"links": {
"self": {
"href": "https://example.com/jsonapi/commerce_order/default/ab42ab1c-b21f-4da2-9e9b-82213e622e59/relationships/coupons"
}
}
},
"shipments": {
"data": [
{
"type": "commerce_shipment--default",
"id": "86c32de5-5cd6-4542-abaf-23065e0458a1",
"meta": {
"drupal_internal__target_id": 209706
}
}
],
"links": {
"related": {
"href": "https://example.com/jsonapi/commerce_order/default/ab42ab1c-b21f-4da2-9e9b-82213e622e59/shipments"
},
"self": {
"href": "https://example.com/jsonapi/commerce_order/default/ab42ab1c-b21f-4da2-9e9b-82213e622e59/relationships/shipments"
}
}
}
}
},
"links": {
"self": {
"href": "https://example.com/jsonapi/commerce_order/default/ab42ab1c-b21f-4da2-9e9b-82213e622e59"
}
}
}
The adjustments are just empty.
So far we have not been able to use PATCH to replace adjustments. They go poof with no error returned. This is on non-draft orders.
I will update our patch on our test site and make sure the removal of AdjustmentPropertyDefinition didn't mess anything up.
rhovland → made their first commit to this issue’s fork.
Here's a preview of how the customer's My Reviews page looks (in a bootstrap theme):
The action links for a user that has already left a review on a product's review page:
Some notes about how the user facing entity list is handled:
The default entity list builder is designed for administrative use. It does not filter the list based on the current user.
The columns presented in the list are relevant for admin users but mostly useless for customers.
As a result it makes more sense to create a separate list builder for customers since the feature set is completely different.
The user list is a route that points to the ProductReviewController to render a page.
The overviewPage() function that gets called to render a page uses the createHandlerInstance() feature of EntityTypeManagerInterface to set a custom handler, in this case a list builder. It then returns a rendered list built by the Entity system.
The one dangling question comes from the comment on createHandlerInstance()
* Usually \Drupal\Core\Entity\EntityTypeManagerInterface::getHandler() is
* preferred since that method has additional checking that the class exists
* and has static caches.
In this case the class always exists because it's part of the module so I'd think a check for that would be unnecessary.
That leaves the question of is this page being output cached? Or do we need to manually cache it?
While it's not clear if assigning storage to a property from a create method has the same problems as when it's done in a constructor I moved to storing the entity type manager in a property instead and querying storage inside the function that uses it just to be safe.
So on our site we're writing new adjustments through the API, not just updating existing ones so we need all of those extra properties to be able to set the characteristics of the adjustment.
Moved the last two commits to separate issues so they can be further evaluated/tested/expanded upon
📌
Safely extend plugin classes
Active
📌
Code quality fixes
Active
rhovland → created an issue.
rhovland → created an issue.
This is going to be fixed in 📌 Drupal 11 compatibility Active
rhovland → created an issue.
Feature all done. Ready for review
Update hook written.
The update fills in the variables and resaves the comment. Once that is done it removes the variables field from the database. I have to read the variables directly from the database because the field no longer exists in the entity.
I tested this update hook on a copy of our production database and examined the table contents before and after and all comments were correctly updated.
Please verify if I have done the translation part in the event subscriber correctly. I used commerce core as a guide.
Comment now shows up on the admin form.
Going to write an update hook next.
If comments on transactions could happen at a separate time from when the transaction was being created I could see it being apart from the entity. But it is part of the creation of the transaction and it makes sense there. Think of it like a comment on a payment (not a payment method) where it's a snapshot in time of something that happened, with additional relevant information attached to it.
If we added comments for gift cards then it would be inappropriate to put it on the giftcard entity.
I'm going to try to tackle this issue and get some code put together for review.
The comments field is definitely useful. Think of it like how commerce_log works. It keeps a list of transactions with details about what happened. The log can be what order a gift card was used on. It can be a gift card was purchased.
What is missing is when a transaction is added by an admin using the "add transaction" button, the comment is blank. That's not good. The admin should be able to enter a comment about the transaction they were creating.
The transaction entity actually already stores the order id associated with the transaction in reference_id
and reference_type
so an additional field would be redundant.
I think we can safely drop the variables field and keep what is already being recorded in the comments but doing it the correct way like this:
'comment' => $this->t('Used on order with id @order_id.', ['@order_id' => $order->id()]),
'comment' => $this->t('Bought @product.' ['@product' => $purchased_entity->label()]),
I did the translation at the entity creation instead of inside getComment() because that was the correct thing to do for a transaction comment. The variables thing is very weird and I did not understand why it was there so I didn't mess with it.
The changes should have actually been:
public function getComment() {
return $this->get('comment')->value;
}
/**
* Registers gift card usage when the order is placed.
*
* @param \Drupal\commerce_order\Entity\OrderInterface $order
* The order.
*/
protected function registerUsage(OrderInterface $order) {
$adjustments = $order->collectAdjustments();
foreach ($adjustments as $adjustment) {
if ($adjustment->getType() != 'commerce_giftcard' || !$adjustment->getSourceId() || $adjustment->getAmount()->isZero()) {
continue;
}
$giftcard = $this->entityTypeManager->getStorage('commerce_giftcard')->load($adjustment->getSourceId());
// Create a transaction for each used gift card.
if ($giftcard instanceof GiftcardInterface) {
/** @var \Drupal\commerce_giftcard\Entity\GiftcardTransactionInterface $transaction */
$transaction = $this->entityTypeManager->getStorage('commerce_giftcard_transaction')->create([
'giftcard' => $giftcard->id(),
'amount' => $adjustment->getAmount(),
'reference_type' => $order->getEntityTypeId(),
'reference_id' => $order->id(),
'comment' => $this->t('Used on order with id @order_id.', ['@order_id' => $order->id()]),
]);
$transaction->save();
}
}
}
So get comment... gets the comment field contents.
And when the transaction is saved the comment is a translatable string using variable substitution. Instead of it being assembled later.
In light of this I think this issue should become
"Create completion pane to display purchased gift cards"
Because right now if a gift card is purchased the customer does not see the code. And shoving it into the variables for the completion message is wrong because now we'd have to override the template for that to fully implement the feature. It should be a separate pane that's added to the complete step of checkout.
As part of this feature we can figure out a better way to handle the query to get the list of gift cards that were purchased.
Ok I think I figured out where the source of the confusion is here.
I thought that the checkout completion message variables were intended to display the gift card codes redeemed to pay for the order (just like how payment methods are usable variables). This code was actually intended to allow to display the codes for purchased gift cards. It does this by querying gift card transactions that have a reference_id
that matches the item id. However this is a very big problem as reference_id is shared by two different types of entity ids!
OrderEventSubscriber::giftcardPurchase()
generates gift card codes for gift cards that were purchased as an order item. It records the order item id in the reference_id
field in commerce_giftcard_transaction.
OrderEventSubscriber::registerUsage()
generates a transaction whenever a gift card is used to pay for an order. It records the order id in the reference_id
field in commerce_giftcard_transaction.
We're storing two different ids in the same field with no way to differentiate the difference between transaction types other than the content of the comment field.
Yeah the patch I wrote was a rough idea that needed more work. I did not have the skill to do much more than what's in it.
Missed a line of code where $rating_element was being set. Link now properly generates.
Still wouldn't explain your problem of stars not showing up since the old code was still creating the rating element. Think it's probably the javascript library missing.
Apologies I had opened this issue to push my code and fell asleep before I finished. I pushed them in the morning. The actual feature this issue is for is done. The only thing remaining to do was the update hook so that existing site configuration is migrated over to the new structure. Also I had not made any changes to the configuration schema file yet which needs to be updated to reflect the changes in configuration.
If the stars aren't displaying then that means the rateit library isn't loading. It's displaying like this on my dev site
rhovland → created an issue.
There was several situations where the add review button should have been displayed but wasn't, or was displayed but shouldn't.
I moved the review type and already reviewed check out of reviewForm() into accessReviewForm() so the "Add your own review" link only shows up when all access checks pass.
I also in a separate commit allowed anonymous users to access the form, where they will be redirected to login so the add link appears to them. This is to invite users to login and leave a review when on the product's review page. The redirect could only be done on the form due to limitations on how drupal handles route access.
Feature done. Needs the schema updated and an update hook written to migrate setting "show_overview_link" to "link_display" = "overview"
rhovland → created an issue.
Ready to go
rhovland → created an issue.
getBalance() effectively cannot return null due to several constraints:
The balance field is defined as required in the Giftcard entity:
$fields['balance'] = BaseFieldDefinition::create('commerce_price')
->setLabel(t('Balance'))
->setDescription(t('Current balance'))
->setRequired(TRUE)
Entity validation enforces this. In GiftcardTest::testSaving() an entity without a balance fails validation.
All code paths that create gift cards set a balance:
- Direct creation sets a balance
- Gift card purchases set a balance
- Bulk generation sets a balance
The Redemption pane has a check for null when it probably should be checking for zero
$balance = $giftcard->getBalance();
if (!$balance) {
$form_state->setError($pane_form, $this->t('Unable to check gift card %code balance, please try again.', ['%code' => $giftcard->getCode()]));
}
Becomes
if ($giftcard->getBalance()->isZero()) {
$form_state->setError($pane_form, $this->t('The provided gift card %code has no balance.', ['%code' => $giftcard->getCode()]));
}
Created a readme file in this issue 📌 Create a readme file Active which includes instructions for creating a purchasable gift card. The instructions are also now on the project's page → so I'm closing this as fixed.
Initial work done. I also copied this to the module's front page to replace the sparse presentation currently there.
Please review for language and clarity and let me know if this is good to commit.
rhovland → created an issue.
rhovland → created an issue.
So while I was looking at the history for this issue to revert the changes made, I ran into this issue's bug. I viewed a comparison, then hit back and then tried to view the comparison again and the button didn't work until I reloaded the page.
Why did you overwrite all of the changes made to this issue?
There is already an issue for this.
📌
Drupal 11 compatibility
Active
Drupal 11 is a requirement for commerce 3.x. Drupal 11 needs to be added to the composer.json. This module has depreciations that need to be fixed for it to be Drupal 11 compatible.
I attempted to address the issue summary. Can you please let me know if there needs to be further changes to it.
In regards to a test, where do you suggest it be placed? It seems FunctionalJavascript tests for forms are located at core/tests/Drupal/FunctionalJavascriptTests/Core/Form
. Should a new test file be created there? Should it be added to an existing Form test?
When writing the test what am I testing for? Am I writing a test to ensure that the submit button is disabled when clicked twice? And then navigate back and ensure that it is not disabled? I was unable to find an existing test for the double click prevention feature.
Lastly, is there a way to ensure someone with more experience with javascript reviews the merge request for correctness? I'm uncertain if I did it correctly. There's also a failure of a nightwatch test that I don't really understand (I've never used nightwatch before).
Appreciate your feedback, I'm still learning and have only done work with tests in contrib modules.
Fixed UPSSdk::getShipmentShopRates so it returns the expected array structure when there is only one rate.
rhovland → created an issue.
I have submitted a MR to fix this upstream
https://www.drupal.org/project/drupal/issues/3144382#comment-16037225
🐛
Prevention of multiple submits blocks use of back navigation
Active
I wrote an event listener for form.js that unsets the double submit data if the page is loaded via the cache (user hits back button), allowing the submit button to work as expected.
If core wants to prevent ever resubmitting a form then it should be a configurable option not a blanket form API feature.
As written this prevents the problem from the original issue, clicking submit multiple times, while avoiding breaking forms when the back/forward buttons are used.
In my testing it affects both Firefox and Chromium.
This affects all submit buttons in a form not just the one that was previously clicked.
So for an example I have a cart form. It has multiple submit buttons. The main one is the checkout button. There are also remove buttons on each cart item and an empty cart button. None of these buttons are functional after clicking back in the browser after submitting the form.
This is the issue where this feature was added
🐛
Double click prevention on form submission
Fixed
The feature was intended to prevent double clicking a submit button causing two form submits from happening. It was not intended to prevent the same form from being submitted again after loading the submission page and going back in the browser.
This feature as written causes unexpected behavior in forms across core and contrib. There is no mechanism to opt out a form from this protection. The only way for an end user to fix the page is to refresh the page or change a form value (if they can).
There is a Drupal 10/11 compatible module here https://www.drupal.org/project/google_customer_reviews →
There is a Drupal 10/11 compatible version here https://www.drupal.org/project/google_customer_reviews →
rhovland → created an issue.
rhovland → created an issue.
rhovland → created an issue.
For more information about the changes in ba662d58 see the following for more information:
Avoid using constructors in extended plugin classes:
https://www.previousnext.com.au/blog/safely-extending-drupal-10-plugin-c...
https://www.drupal.org/node/3468511 →
https://www.drupal.org/node/3076421 →
Assigning entity storage to a property instead of the entity manager:
https://mglaman.dev/blog/dependency-injection-anti-patterns-drupal
I'm going to put some time into evaluating janephp and openapi-generator and create a library for USPS using one of these generators. Using a generator will also help with adapting to subsequent changes to the API as we can just feed the new spec into it and audit the changes made for accuracy.
Once the library is made I'll create a new version of the USPS module and have the maintainers merge that into this project.
This is going to take a few months as I will be working on this in the spare time between projects at work. But now that there's an end date the urgency of this has placed this higher on my list.
Ok looks like we now have a end of service date
"The Web Tools API platform will be retired on January 25, 2026."
https://www.usps.com/business/web-tools-apis/welcome.htm
The first thing that needs to happen is a PHP API client being made
Once there is a client (the script I linked is not sufficient) then a new major version of this module can be created and adjusted to use the API client.