formie icon indicating copy to clipboard operation
formie copied to clipboard

EVENT_MODIFY_PAYLOAD Never Fires for Opayo Integration

Open MattAppleton opened this issue 4 months ago • 1 comments

Describe the bug

After upgrading from Craft 4/Formie 2.x to Craft 5/Formie 3.x, the EVENT_MODIFY_PAYLOAD event handler for Opayo payment integration never executes, even though the module initializes successfully. This prevents custom field mapping from working, resulting in "Missing mandatory field" errors from Opayo API.

Steps to reproduce

  1. fill in a payment form for Opayo using standard payment block in Formie
  2. watch it fail

Form settings

  • Multi-page form: No
  • Submission Method: Ajax
  • Client-side Validation: Yes
  • Custom Form Templates: Yes

Craft CMS version

5.8.19 (upgraded from 4.16.15)

Plugin version

3.1.5 (upgraded from 2.x)

Multi-site?

Yes

Additional context

Formie's getValueForIntegration() method returns null for nested fields (Name, Address) in Formie 3.x, causing payment submission to fail with missing field errors. A custom module was created to manually map these fields using EVENT_MODIFY_PAYLOAD, but the event handler callback never executes.

  • ✅ Updated from EVENT_BEFORE_MAKE_PAYMENT to EVENT_MODIFY_PAYLOAD
  • ✅ Updated from $event->paymentData to $event->payload
  • ✅ Confirmed event constant exists in vendor/verbb/formie/src/integrations/payments/Opayo.php:47
  • ✅ Confirmed event is triggered in Opayo.php:210

Current Module Code

File: /modules/OpayoFix/OpayoFix.php

<?php
namespace modules\OpayoFix;

use Craft;
use verbb\formie\integrations\payments\Opayo;
use yii\base\Event;
use yii\base\Module;

class OpayoFix extends Module
{
    public function init()
    {
        parent::init();

        \Craft::error('OpayoFix module init() called - STARTING', 'opayo-fix');
        \Craft::info('OpayoFix module init() called - STARTING', 'opayo-fix');

        // Register event handler IMMEDIATELY
        $this->registerOpayoEventHandler();

        Craft::info('OpayoFix module loaded and ready', 'opayo-fix');
    }

    private function registerOpayoEventHandler()
    {
        \Craft::info('OpayoFix: Registering EVENT_MODIFY_PAYLOAD handler', 'opayo-fix');

        // Hook into Opayo's payment processing
        Event::on(
            Opayo::class,
            Opayo::EVENT_MODIFY_PAYLOAD,
            function($event) {
                \Craft::error('OpayoFix: EVENT FIRED!!!', 'opayo-fix');
                $submission = $event->submission;

                // Get field values
                $nameData = $submission->getFieldValue('name1');
                $addressData = $submission->getFieldValue('address');
                $emailData = $submission->getFieldValue('email');

                // Map to Opayo format
                if ($nameData && is_array($nameData)) {
                    $event->payload['customerFirstName'] = $nameData['firstName'] ?? '';
                    $event->payload['customerLastName'] = $nameData['lastName'] ?? '';
                }

                if ($addressData && is_array($addressData)) {
                    $event->payload['billingAddress'] = [
                        'address1' => $addressData['address1'] ?? '',
                        'city' => $addressData['city'] ?? '',
                        'postalCode' => $addressData['zip'] ?? '',
                        'country' => $addressData['country'] ?? ''
                    ];
                }

                if ($emailData) {
                    $event->payload['customerEMail'] = $emailData;
                }
            }
        );
    }
}

The Problem

Module init() is called successfully, but only the FIRST log line appears:

  • ✅ Line 16: 'OpayoFix module init() called - STARTING' APPEARS
  • ❌ Line 20: $this->registerOpayoEventHandler(); NEVER EXECUTES (or silently fails)
  • ❌ Line 22: 'OpayoFix module loaded and ready' NEVER APPEARS

This suggests:

  1. Either PHP execution stops after line 17 (unlikely - no errors)
  2. Or there's an OPcache issue serving old bytecode (despite multiple cache clears)
  3. Or there's a Yii2/Craft timing issue with event registration

Form Submission Data

The form IS submitting correct data (from formie-2025-11-06.log):
'fields' => [
    'name1' => [
        'prefix' => 'mr',
        'firstName' => 'xxx',
        'lastName' => 'xxx'
    ],
    'address' => [
        'address1' => 'xxx',
        'city' => 'London',
        'zip' => 'xxx',
        'country' => 'GB'
    ],
    'email' => 'mxxx.com'
]

But Opayo receives:
{
  "customerFirstName": null,
  "customerLastName": null,
  "billingAddress": {
    "address1": "",
    "city": "",
    "postalCode": "",
    "country": null
  }
}

MattAppleton avatar Nov 06 '25 14:11 MattAppleton

So that does seem to be working for me, using your code. I can see

\Craft::error('OpayoFix: EVENT FIRED!!!', 'opayo-fix');

Producing content in the logs, as well as the payload being modified. I've even tested with 3DS challenge and without, just to be sure.

You'll want to watch referencing fields like $submission->getFieldValue('name1'); because if a field with that handle doesn't exist, you'll get an error thrown.

I know you've mentioned issues with the email/billing mapping during the migration, but any reason you're not using this which would handle this sort of thing?

engram-design avatar Nov 06 '25 23:11 engram-design