Account created on 16 March 2023, over 1 year ago
#

Recent comments

The issue is supporting creating payment methods 'off-checkout'. Opayo Pi supports it, Drupal Commerce supports it but this gateway doesn't currently implement it.

(It could be added but I suspect it's a fair chunk of work, which I won't have any time for in the foreseeable and I'm not getting the feeling anybody else is keen and available to jump in and add it.)

In an ideal world I would take out the inheritance of 'SupportsCreatingPaymentMethodsInterface' but it looks like that would probably break the existing normal checkout for this gateway.
I don't think Drupal Commerce has advanced yet to naturally support a payment process like Opayo Pi where most of the heavy lifting is
done between the customer's browser Javascript and the Opayo servers.

Instead I might add some code to identify and intercept when a customer tries to add a payment method 'off-checkout' (e.g. on the user's 'Payment Methods' tab) and display a nice 'Not available' or 'Not supported' message.

I have not implemented any 'stored payment method' functionality (and am currently not intending to since my own customer base doesn't need it).
I have just checked and the Gateway is inheriting from 'SupportsStoredPaymentMethodsInterface' so that's probably the issue.

I will need to refactor the inheritance slightly to remove 'SupportsStoredPaymentMethodsInterface' from the inheritance chain.

Yes, fair point.

I would need to test to be 100% sure but must assume that a missing CVV normally means the 'card tokenisation' step would fail in production. This would then get reported back to the module's javascript, this then posts Opayo's error message back to the back-end where it should get translated into a Drupal error message.

So ... (unless I'm wrong about the above) the transaction wouldn't proceed in production.
Obviously checking for a non-empty CVV is straightforward: reporting back to the Drupal form javascript is where I'd have to do a bunch of digging.

Closing the issue. I've fixed - as far as I can ascertain - the issues that were tracked down and reported and haven't heard any more about the earlier and vaguer ones.

I've just tested the 'beta8' release against the Opayo live server on one of my setups and the transaction went through fine.

There's a 'beta6' release now that has the added verbose debug lines.
Also a fix to the code dealing with 3DS result for anonymous users.

Either there's an issue with test vs live 'mode' in the back-end or it might be a caching issue in the browser.

I've pushed a commit that adds some logging of the 'mode' to the verbose logging.
You should see an additional line in the console log - just before the javascript goes and contacts Opayo about tokenisation.
There's also an additional line to the back-end log, something like
PaymentMethodAddForm::buildOpayoPiOwnForm - mode: test, log level: 10

Could you replace the 2 files, test again and let me know the log results

'Beta5' release has got the fix to deal with the colordepth issue

... furthermore, have had a look again at the API documentation and there are more details about the request payload, particularly the StrongCustomerAuthentication section than I remember.
I suspect it might throw up more issues for you until I get that part of the code checked and updated.

Opayo doesn't seem to like the value for browserColorDepth. In your testing this was value '30', supplied by the browser to the payment gateway backend and forwarded with the payment transaction request to Opayo.

I've just checked Opayo's API again and I think they've added some details: there are only certain color depths acceptable and '30' isn't one of them.

I will change line 1629 of OpayoPiPaymentGateway.php to:

        if (array_search($extra_info->colorDepth, ['1', '4', '8', '15', '16', '24', '32', '48']))
          $strongCustomerAuthentication->browserColorDepth = $extra_info->colorDepth;
        else
          $strongCustomerAuthentication->browserColorDepth = 32;

It's in the api reference, look for 'Create a Transaction'

One of its request payload parameters is:

customerPhone
required
string (customerPhone) <= 19 characters
Customer's home phone number (this could also be the same as their mobile phone number if they do not have a home phone). The customerPhone must be in the format of + and country code and phone number. Example: For a UK phone number of 03069 990210, you will submit the following: +443069990210. This must be provided for 3D Secure checking and fraud screening purposes.

Release 4 has the updated javascript. Please keep the verbose logging for now, it's the best tool to get diagnostic info (well, without engaging actual debuggers) to me.

Beta4 release: you can now have a 'Telephone Number' field generated under the card details on the checkout and take the phone number off the 'Customer' profile type (you can leave it on but un-check required if you want)

I'm going to introduce some options to sidestep the issue.

it will be possible to have a telephone field with the payment details fields, configurable:

* As a plain text field (default): easy to set up but customer will need to type in international format '+44.....' themselves
* Using a 'Phone International' type field, looks nice with the flag and should normally reformat the input to 'international' format but needs site admin to properly configure 'Phone International'

Anybody who would rather have the current approach of putting a telephone field with the 'Customer' profile type can still have that, with some provisos:
* If you make the field required then it kind of interferes with the auto-recalculation of the shipping rates. This is just how that bit of commerce shipping logic is done at the moment outside of my remit.
* If you don't make the field required then the customer could leave it empty. If they tried to submit it would then show a notice that the field should be filled in.

Did a whole day of testing myself yesterday, for the first time on a test setup with shipping enabled.
Looking at the logs, similar to mine.
I found an issue yesterday while testing:

Both the 'dropin' style javascript and 'own form' use javascript to trigger the form submit rather than a physical click.
However, with Drupal forms, submitting a form by javascript is not enough because it seems to be missing out on collecting the
settings that tell the back-end the form has been submitted (there are some comments in elementTriggeredScriptedSubmission in Drupal's FormBuilder.php)
Instead we explictily tell the backend about the form triggering element by setting the '_triggering_element_name' form field in the javascript. However (and this is the issue I found) we need to set '_triggering_element_value' as well because on checkout with additional delivery related form elements it was getting confused about which button triggered the submit and never progressing to the 'submit' handler of the payment gateway form.

I'm working on a new release but it you wanted to test in the meantime, replace lines 284-290 of opayo-pi-ownform.js with this

if (ds.commerceopayopi.opayoCheckout.hasOwnProperty('useTriggeringElement')) {
          // Set the triggering element to the submit button
          input = document.createElement("input");
          input.setAttribute("type", "hidden");
          input.setAttribute("name", "_triggering_element_name");
          input.setAttribute("value", ds.commerceopayopi.opayoCheckout.submitButton.getAttribute("name"));
          checkoutForm.appendChild(input);
          input = document.createElement("input");
          input.setAttribute("type", "hidden");
          input.setAttribute("name", "_triggering_element_value");
          input.setAttribute("value", ds.commerceopayopi.opayoCheckout.submitButton.value);
          checkoutForm.appendChild(input);
        }

If it's a guest user I don't think we can rely on there being a profile available and we would still need the cardholder's 'First Name' and 'Last Name' fields. The only tweak possible, in that case, would be to remove the 'Cardholder Name' field and create it on-the fly, where needed, from the 'First Name' and 'Last Name'. I could add an option to the config to make it behave that way.

For a logged in user, yes, a possible behaviour is that it assumes the profile first and last name is the cardholder. Again, I could add a toggle to make it behave that way.

In both cases I would keep the current behaviour as the default, because I think it's the 'safest' way of doing it - i.e. without baking in certain assumptions about the cardholder.

I've added a whole lot of logging that I'm hoping will help pinpoint the issue.

Commit '2f451c17' - please note it's not tagged so you won't be able to use composer to install.

There's a configuration setting for the payment gateway called 'Gateway log level', set this to 'Extra Verbose Debugging Info'.

This will cause verbose logging to the browser console and the Drupal log.

For the browser console, make sure you have 'Preserve Log' (Chrome) checked so that it keeps the 'history'.

For Drupal I strongly suggest installing and enabling File Logger ( https://www.drupal.org/project/filelog ) and log to the following
specific channels: commerce_opayo_pi, commerce_payment.

An example where I deliberately put in an illegal value for the 'cardholder name'.

Browser console log shows:

opayo-pi-ownform.js?v=1:346 OpayoPiOwnForm form submit button clicked, form build id: form-dXHdCYdJOjqFeKy8xJ_sTAtcP_v_nMt2UOJ3ys0W6Wo
opayo-pi-ownform.js?v=1:346 OpayoPiOwnForm request merchant session key using URL: /commerce_opayo_pi/merchantsessionkey/12/tokenize_and_submit, form build id: form-dXHdCYdJOjqFeKy8xJ_sTAtcP_v_nMt2UOJ3ys0W6Wo
opayo-pi-ownform.js?v=1:346 OpayoPiOwnForm request merchant session key sent (URL: /commerce_opayo_pi/merchantsessionkey/12/tokenize_and_submit), form build id: form-dXHdCYdJOjqFeKy8xJ_sTAtcP_v_nMt2UOJ3ys0W6Wo
opayo-pi-ownform.js?v=1:346 OpayoPiOwnForm - Ajax command opayoMerchantSessionKey: returned merchant session key: 56E97238-664F-43F9-B259-FFE4C7BF7B57, expiry: 2024-01-26T16:18:57.053Z, instruction: tokenize_and_submit, form build id: form-dXHdCYdJOjqFeKy8xJ_sTAtcP_v_nMt2UOJ3ys0W6Wo
opayo-pi-ownform.js?v=1:346 OpayoPiOwnForm.callCardDetailTokenisation: about to call sagepayOwnForm.tokeniseCardDetails with merchant session key: 56E97238-664F-43F9-B259-FFE4C7BF7B57 for card: *********0006, form build id: form-dXHdCYdJOjqFeKy8xJ_sTAtcP_v_nMt2UOJ3ys0W6Wo
opayo-pi-ownform.js?v=1:346 OpayoPiOwnForm.callCardDetailTokenisation: called sagepayOwnForm.tokeniseCardDetails with merchant session key: 56E97238-664F-43F9-B259-FFE4C7BF7B57, form build id: form-dXHdCYdJOjqFeKy8xJ_sTAtcP_v_nMt2UOJ3ys0W6Wo
sagepay.js:14     POST https://sandbox.opayo.eu.elavon.com/api/v1/card-identifiers 422 (Unprocessable Entity)
opayo-pi-ownform.js?v=1:346 OpayoPiOwnForm.cardDetailsTokenised result: false, merchant session key used: 56E97238-664F-43F9-B259-FFE4C7BF7B57, form build id: form-dXHdCYdJOjqFeKy8xJ_sTAtcP_v_nMt2UOJ3ys0W6Wo
opayo-pi-ownform.js?v=1:346 OpayoPiOwnForm.cardDetailsTokenised set card identifier result to: false
opayo-pi-ownform.js?v=1:346 OpayoPiOwnForm.cardDetailsTokenised failed response processed - about to submit form, HTTP error code: 422, first error (code: 1005): The cardholder name contains invalid characters
opayo-pi-ownform.js?v=1:346 OpayoPiOwnForm.cardDetailsTokenised failed response processed - form submitted
Navigated to http://drupal-dev5.ihbristol.com/checkout/12/order_information

Output from the corresponding Drupal file log:

[Fri, 01/26/2024 - 16:12] OpayoController::MerchantSessionKey requesting new merchant session key
[Fri, 01/26/2024 - 16:12] OpayoPiPaymentGateway::requestMerchantSessionKey: Before merchant session key request, mode: test
[Fri, 01/26/2024 - 16:12] OpayoPiPaymentGateway::requestMerchantSessionKey: Successful merchant session key response, mode: test
[Fri, 01/26/2024 - 16:12] [Info] [commerce_opayo_pi] [client: 192.168.200.147, admin] OpayoPiPaymentGateway::requestMerchantSessionKey: new merchant session key: 56E97238-664F-43F9-B259-FFE4C7BF7B57 (expiry: 2024-01-26T16:18:57.053Z), mode: test
[Fri, 01/26/2024 - 16:12] OpayoPiPaymentGateway::getNewMerchantSessionKey: sequence nr: 2 for session key: 56E97238-664F-43F9-B259-FFE4C7BF7B57 (expiry: 2024-01-26T16:18:57.053Z) for order 12 (uid: a2ed16f4-36c7-4dd6-a65a-5c423e131df8), mode: test
[Fri, 01/26/2024 - 16:12] OpayoController::MerchantSessionKey returned merchant session key settings: @"merchantSessionKey":"56E97238-664F-43F9-B259-FFE4C7BF7B57","merchantSessionKeyExpirySecs":400,"merchantSessionKeyExpiry":"2024-01-26T16:18:57.053Z","paymentGateway":"opayo","orderId":"12","instruction":"tokenize_and_submit"
[Fri, 01/26/2024 - 16:12] PaymentMethodAddForm::validateConfigurationForm - payment method bundle: credit_card_opayo, form id: commerce_checkout_flow_opayo_checkout_flow, form build id: form-dXHdCYdJOjqFeKy8xJ_sTAtcP_v_nMt2UOJ3ys0W6Wo, form token: GAsIScUIXchTCungS6sn_c_1mAZz-Yp-2-51UX6Ft-c
[Fri, 01/26/2024 - 16:12] PaymentMethodAddForm::validateOpayoPiOwnForm - card tokenisation attempted: 1 (form build id: form-dXHdCYdJOjqFeKy8xJ_sTAtcP_v_nMt2UOJ3ys0W6Wo)
[Fri, 01/26/2024 - 16:12] PaymentMethodAddForm::validateOpayoPiOwnForm - card tokenisation result: false (form build id: form-dXHdCYdJOjqFeKy8xJ_sTAtcP_v_nMt2UOJ3ys0W6Wo)
[Fri, 01/26/2024 - 16:12] PaymentMethodAddForm::validateOpayoPiOwnForm - HTTP error code: 422 (form build id: form-dXHdCYdJOjqFeKy8xJ_sTAtcP_v_nMt2UOJ3ys0W6Wo)
[Fri, 01/26/2024 - 16:12] PaymentMethodAddForm::validateOpayoPiOwnForm - error detail 0: code 1005, message The cardholder name contains invalid characters (form build id: form-dXHdCYdJOjqFeKy8xJ_sTAtcP_v_nMt2UOJ3ys0W6Wo)
[Fri, 01/26/2024 - 16:12] PaymentMethodAddForm::validateOpayoPiOwnForm - filter errors  (form build id: form-dXHdCYdJOjqFeKy8xJ_sTAtcP_v_nMt2UOJ3ys0W6Wo)
[Fri, 01/26/2024 - 16:12] PaymentMethodAddForm::validateOpayoPiOwnForm - no card identifier returned, http error code: 422 (form build id: form-dXHdCYdJOjqFeKy8xJ_sTAtcP_v_nMt2UOJ3ys0W6Wo)
[Fri, 01/26/2024 - 16:12] PaymentMethodAddForm::validateOpayoPiOwnForm - detected validation error feedback  (form build id: form-dXHdCYdJOjqFeKy8xJ_sTAtcP_v_nMt2UOJ3ys0W6Wo)
[Fri, 01/26/2024 - 16:12] PaymentMethodAddForm::validateConfigurationForm finished, nr validation errors detected: 1, form build id: form-dXHdCYdJOjqFeKy8xJ_sTAtcP_v_nMt2UOJ3ys0W6Wo

In the example above Opayo reports issues with the tokenisation of the card details: "HTTP error code: 422, first error (code: 1005): The cardholder name contains invalid characters"

This gets returned with a form submit (in a hidden field) and should be picked up by PaymentMethodAddForm::validateConfigurationForm
"nr validation errors detected: 1".

You should then see 1 or more warning messages returned above the checkout form.

It would be good to know if it got from step (4) to step (5), i.e. whether the form submit is taking place.
If you've got access to your web server logs then look out for the following 2 log entries:

"POST /commerce_opayo_pi/merchantsessionkey/141/tokenize_and_submit?_wrapper_format=drupal_ajax HTTP/1.1" 200 286 "http://drupal-dev3.ihbristol.com/checkout/141/order_information"

"POST /checkout/141/order_information HTTP/1.1" 303 454 "http://drupal-dev3.ihbristol.com/checkout/141/order_information"

"POST /commerce_opayo_pi/merchantsessionkey/" is step (2)
If we get to step (5) there should be a follow-up entry "POST /checkout//order_information"

All are required by Opayo at different stages:

We need 'Cardholder Name' (not divided into first name and second name) for card tokenisation.
Then later, the API for submitting a transaction requires separate 'First Name' and 'Last Name' fields.

The 'Drop in form method' will still ask for the separate first name and last name (in addition to the cardholder name), they are just part of the 'Address'

I've experimented with just displaying 'first name' and 'last name' but decided against.

Normally, when you can't progress from the 'Payment Information' page, it's because there are validation issues.
However, I would expect there to be 'feedback' saying what field was the issue.

The sequence (simplified), after the customer clicks 'Continue to review' is:

(1) javascript intercepts and uses AJAX to call /commerce_opayo_pi/merchantsessionkey/....
(2) back-end requests a merchant session key from Opayo and returns it to the javascript
(3) javascript calls Opayo with the merchant session key and card details to tokenise and gets a 'card identifier' back
(4) javascript copies 'card identifier' into hidden form field (along with a bunch of other things) and submits the form
(5) method 'validateConfigurationForm' on class 'PaymentMethodAddForm' gets called as a result of the form submit

This checks the submitted fields, if it finds any issues it sets some 'errors' which cause the form to get redisplayed with error messages.

If there are no issues it (6) saves the details in a new 'payment method' and continues onto the 'Review' checkout page (if enabled).

It would be good if we could establish where in the (1)-(6) things are getting stuck.
Can you provide anything that could help me reproduce?

From about step (6) onwards the most useful (outside of XDebug sessions) diagnostic are the log messages.
Could you install the file log module ( https://www.drupal.org/project/filelog )
and have it log cannels 'commerce_opayo_pi' and 'commerce_payment', see 'filelogsettings.png'

Log will be /drupal/web/sites/default/files/logs/drupal.log

if the issue is with step (5) then I may temporarily need to add a bunch more logging to the validation.

I don't think the console error is related to the problem.

When you say 'direct integration' I'm assuming you mean: with the '3D Secure challenge inside iFrame' payment gateway setting un-checked.

Assuming the 'Review' step is part of your checkout flow, do you see the 'Review' checkout pane?

That should show a 'Pay and complete purchase' button.

If you click you should see the '3D Secure' checkout pane with a 'Proceed to 3D Secure' button.

Clicking that should forward you to the customer's bank's 3D secure screen.

Where exactly does it stop and what have you got in corresponding log messages?

At the end of the day I need a non-ambiguous place to define settings for Opayo of which there should only be 1 instance, irrespective of how may 'opayo' gateways get created.
I need these settings for the maintenance routines.
Conceptually I would argue those 'maintenance' settings don't go with the payment gateway settings.
For the API credentials I think you can argue either way: keep them with the 'generic' settings because that avoids duplication or keep them with the gateway settings because it's more intuitive.

I've introduced an option in beta3 to have the 3D Secure interaction outside the iframe. This should help make it more obvious what's going on. IMO it will have to be combined with using the browser's 'Develop tools' to keep track of what's going on in the browser, also the request log on the server and the drupal log (or XDebug). Without that level of detail I can't diagnose further.

Updated composer.json in beta3. Installs OK for me in new D10.2.

Comment 7 was not directly related to the issue so please ignore.

Maybe it's best if I add a toggle to have the 3D secure inside an iFrame or not.

Then ... if not inside an iFrame the customer/tester will get taken away from your site completely during the 3D secure interaction.
Not pretty, imo, but perhaps better for testing and troubleshooting.

I've updated TransactionListBuilder.php to deal with cases where the order can't be found.
Seems to work OK now on my end.

Please note: part of the refactoring in 1.0.0-beta2 moves a bunch of the configuration fields from the 'payment gateway' to a new settings form for the module (path /admin/commerce/config/opayo_pi/settings).
This allows multiple 'opayo_pi' payment gateways (which is something I'll need myself but I appreciate not needed for most).

Can you work out on your if there was any 3D Secure redirect.

The 3DS checkout pane is logged on the webserver log and the Drupal log.
Webserver log:
GET /checkout/{order id}/threedsecure
Drupal log:
Opayo3DSReview::buildPaneForm

You can use the 'Network' tab on Chrome Developer Tools to see what might be
happening inside the iframe but it's effectively out of our hands until we get a redirect or my 20 minute timeout expires.

If you got anything returned from 3D secure it would show in the Drupal log with a message containing
'OpayoController::Process3DSecure: from {customer IP address}

A matching web server log would be
POST /commerce_opayo_pi/3dSecure/{order_id}

By the way, there's an issue with the current code where the 'payment gateway id' has been hardcoded in a few places.
I'm obviously refactoring to sort this out at the moment.
This could be the cause, depending on where your payment attempt gets stuck and how you've configured things.

The button 'Proceed to 3D Secure' simply has a link to the acquiring bank's 3D secure.
The URL for this link is provided by Opayo.

First of all we need to check/rule out issues with the URL.

You should be able to find the URL that Opayo instructs us to present by looking at the log message that contains 'transaction request response received' like below. In the test environment Opayo instructs us to present URL "https://test.sagepay.com/3ds-simulator/html_challenge" to the user.

[Wed, 12/13/2023 - 15:38] [Info] [commerce_opayo_pi] [client: 192.168.25.20, Anonymous] OpayoPiPaymentGateway::submitNewOpayoPiTransaction: order 141 (uid: c9ec659d-c43e-47f2-88d7-5f94121ea0a9), mode: test, transaction request response received: https://pi-test.sagepay.com/api/v1/transactions/, response payload: {"statusCode":"2021","statusDetail":"Please redirect your customer to the ACSURL to complete the 3DS Transaction","transactionId":"EA8B43FD-5B8F-EB00-B519-5008C82F5699","acsUrl":"https://test.sagepay.com/3ds-simulator/html_challenge","acsTransId":"21b3a4fe-2ad6-4843-82d7-f15057aff810","dsTransId":"96f3bfff-578f-4a40-9a9a-de4e67a18e2e","status":"3DAuth","cReq":"ewogICJtZXNzYWdlVHlwZSIgOiAiQ1JlcSIsCiAgIm1lc3NhZ2VWZXJzaW9uIiA6ICIyLjIuMCIsCiAgInRocmVlRFNTZXJ2ZXJUcmFuc0lEIiA6ICI2ZGJiMmI5NC02YTZjLTRkMDAtOGRiNy05ZjAzYWE4OThiNTAiLAogICJhY3NUcmFuc0lEIiA6ICIyMWIzYTRmZS0yYWQ2LTQ4NDMtODJkNy1mMTUwNTdhZmY4MTAiLAogICJjaGFsbGVuZ2VXaW5kb3dTaXplIiA6ICIwMSIKfQ"}
[Wed, 12/13/2023 - 15:38] [Info] [commerce_payment] [client: 192.168.25.20, Anonymous] Opayo3DSReview::buildPaneForm: convert transaction ID EA8B43FD-5B8F-EB00-B519-5008C82F5699 to BASE64: RUE4QjQzRkQtNUI4Ri1FQjAwLUI1MTktNTAwOEM4MkY1Njk5 for 3DS challenge (order 141, uid: c9ec659d-c43e-47f2-88d7-5f94121ea0a9)

In 'live' this will be a URL associated with the customer's card bank.
First of all it's probably worth double-checking the URL and what actually gets sent if you press it. Obviously Chrome/Firefox 'Developer tools' is your friend here.

If there's an issue with the URL or the payload then I need some more details.

If the issue is with the bank-supplied URL or what happens once you press it then you may have to get Opayo involved: once the
button is pressed it's out of the control of the payment gateway until we get a redirect to
the /commerce_opayo_pi/3dSecure/{order} URL (which I assume never happened when the fault occured)

This implies that the opayo_transaction doesn't have an associated commerce_order. I suppose it can happen if the order is manually deleted.

looks like I need to add

$query->accessCheck(FALSE);

wherever I load entities from the database (my test setup happens to be D9 so absense of the check was being quietly tolerated)

Here is a typical extract from my Drupal log for a successful payment against Opayo's test system:

Web server log

192.168.25.20 - - [13/Dec/2023:15:38:22 +0000] "POST /commerce_opayo_pi/merchantsessionkey/141/tokenize_and_submit?_wrapper_format=drupal_ajax HTTP/1.1" 200 286 "http://drupal-dev3.ihbristol.com/checkout/141/order_information" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0"
192.168.25.20 - - [13/Dec/2023:15:38:23 +0000] "POST /checkout/141/order_information HTTP/1.1" 303 454 "http://drupal-dev3.ihbristol.com/checkout/141/order_information" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0"
192.168.25.20 - - [13/Dec/2023:15:38:23 +0000] "GET /checkout/141/review HTTP/1.1" 200 47126 "http://drupal-dev3.ihbristol.com/checkout/141/order_information" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0"
192.168.25.20 - - [13/Dec/2023:15:38:28 +0000] "POST /checkout/141/review HTTP/1.1" 303 458 "http://drupal-dev3.ihbristol.com/checkout/141/review" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0"
192.168.25.20 - - [13/Dec/2023:15:38:29 +0000] "GET /checkout/141/payment HTTP/1.1" 302 350 "http://drupal-dev3.ihbristol.com/checkout/141/review" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0"
192.168.25.20 - - [13/Dec/2023:15:38:29 +0000] "GET /checkout/141/threedsecure HTTP/1.1" 200 45414 "http://drupal-dev3.ihbristol.com/checkout/141/review" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0"
192.168.25.20 - - [13/Dec/2023:15:38:40 +0000] "POST /commerce_opayo_pi/3dSecure/141 HTTP/1.1" 200 32759 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0"
192.168.25.20 - - [13/Dec/2023:15:38:41 +0000] "POST /checkout/141/threedsecure HTTP/1.1" 302 334 "http://drupal-dev3.ihbristol.com/checkout/141/threedsecure" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0"
192.168.25.20 - - [13/Dec/2023:15:38:41 +0000] "GET /checkout/141/complete HTTP/1.1" 200 36264 "http://drupal-dev3.ihbristol.com/checkout/141/threedsecure" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0"

Drupal log
(attached)

Assuming you're using the 'own form' setting, things normally start with the browser asking the back-end to ask Opayo for a merchant session key, in the first 'POST /commerce_opayo_pi/merchantsessionkey/141/tokenize_and_submit' (see above).

I'm assuming things get partway through, at least to the point where a transaction is registered with Opayo, so I would expect you to have at least a partial set of log messages matching the ones in the attached log file.

I've got transactions working end-to-end for 'own form' Opayo pi method, including if there's a 3D secure step, on the test environment.
Lots left to do: refunds, reporting, testing all error cases and general tidying up. I'm guessing another 2-3 weeks.
Not doing repeat transactions because we don't need them at the moment.
There's a copy here: https://www.ihbristol.com/sites/default/files/commerce_opayo_pi.zip, but it's very much *not* production ready.

There is a lot of 'new' stuff in the module I'm working on:
* Javascript to try to transparently deal with expiration of merchant session keys.
* Opayo want 'cardholder' field for tokenisation but additionally 'first name' and 'last name' for the strong customer authentication.
I've ended up adjusting the form fields to present this to the user in a hopefully reasonably intuitive way.
* Extra step to the checkout flow for 3D Secure authentication with associated custom CheckoutFlow plugin class and resolver
* New checkout pane to hold the iframe for 3DS
* Javascript to go along with 3DS.
* New entity to capture Opayo transaction info
* Trying to put in a lot of logging

Wouldn't know where to start if I had to back-port all of that to the existing module.
Happy to share my source if somebody else wants to tackle that.

Country needs to be set on the Elavon developer website (https://developer.elavon.com) to 'United Kingdom' first.
Then 'API Docs' > 'Partner APIs' > 'Online Payments on Opayo'.

Opayo 'Form', 'Server', 'Shared' and 'Direct' are under 'Legacy APIs'

Production build 0.69.0 2024