PRE-3551: create payment with Hosted Fields - #311
Draft
adumont-payplug wants to merge 1 commit into
Draft
Conversation
adumont-payplug
marked this pull request as draft
August 12, 2026 13:40
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Wires the
payplug_uhf(Unified Hosted Fields) gateway to actually create and confirm payments through PayPlug's Unified API via UPC (payplug/unified-plugin-core), replacing PRE-3550's stub that only stored the hosted-fields token and force-completed checkout without ever calling a payment API.Motivation:
PRE-3550 tokenizes a card client-side into an
hfToken(+ selected brand + save-card flag) and PRE-3553 added the gateway configuration, but nothing between the two actually creates a payment:NullHostedFieldsPaymentProcessorjust stores the token onPayment::detailsand bypasses Payum's Capture/Notify/Status pipeline entirely. This PR closes that gap end-to-end: Capture creates the payment via the Unified API, redirects for 3DS/SCA when required, and the asynchronous UPC webhook confirms the final outcome and updates the Sylius payment/order state — with all payment/outcome-mapping logic living inunified-plugin-core, not duplicated in the plugin.Related issue(s): Closes PRE-3551
What changed
src/Upc/) implementing UPC contracts that didn't have one yet:SyliusUpcLogger→ILogger(wraps the existingmonolog.logger.payplugchannel)SyliusUpcConfigurationRepository→IConfigurationRepository(reads thepayplug_uhfgateway's Payum config — OAuthclient_id/client_secret, public key material)SyliusUnifiedApiHttpClient→IUnifiedApiHttpClient(adapts Symfony'sHttpClientInterface, same transport-exception handling asSyliusOAuthHttpClient)SyliusUpcLock→ILock(non-blocking wrapper over SymfonyLockFactory, for webhook idempotency)SyliusPaymentOperationRepository→IPaymentRepository(backed by the newPayPlugOperationentity)SyliusOrderStateMutator→IOrderStateMutator(mapsPaymentOutcome::*to SyliusPaymentTransitionsvia the state machine)PayPlugOperationentity + migration (Version20260810120000), persisting each webhook operation (orderId,operationId,execCode,outcome,treated,createdAt) for idempotent webhook processing.payplug_uhf:CaptureHostedPaymentRequest/Handler/CommandProvider— builds aHostedFieldDtofrom the storedhfToken/selectedBrandplus order/customer/browser context, callsUnifiedApiHostedPaymentService::createHostedPayment(), and either redirects for 3DS or leaves the payment "processing" pending the webhook.payplug_uhf:NotifyHostedPaymentRequest/Handler/CommandProvider— a new, parallel handler (doesn't reuse the legacyNotifyPaymentRequestHandler, which is built around the old SDK's response shape). Verifies the webhook viaWebhookNotificationHelper, guards against concurrent/retried delivery withILock, persists the operation, and applies the outcome viaIOrderStateMutator.payplug_uhf:StatusHostedPaymentRequest/Handler/CommandProvider— no polling; just reflects whatever state the webhook has already applied.PostPaymentSelectEventSubscriber: Hosted Fields now redirects tosylius_shop_order_paylike every other gateway, instead of the oldsylius_shop_order_showbypass that skipped Payum entirely.CaptureHttpResponseProvider: tagged forpayplug_uhf(the tag PRE-3550 left out).composer.json: bumpspayplug/unified-plugin-coreto1.0.0-rc0(shipscreateHostedPayment()/WebhookNotificationHelper, which this PR depends on).config/services.yaml: wires the six new adapters, thepayplug_uhfcommand-provider/http-response-provider services, and new config parameters (payplug.unified_api_base_url,payplug.uhf_account_id,payplug.uhf_submerchant_external_id).Known limitations (deferred on purpose)
Cardpersistence for saved UHF cards.UpdatePaymentStateCommand.payplug.uhf_account_id/payplug.uhf_submerchant_external_idare temporarily hardcoded via env-var defaults for QA (see theTEMPORARY (PRE-3551 QA testing)comments inconfig/services.yaml) — real per-merchant account resolution is a follow-up.features/shop/hosted_fields_payment_and_webhook_flow.feature) documents the two target end-to-end scenarios, but several steps are marked# NEW — not yet defined: the Mocker/step-definition infrastructure they need doesn't exist yet, so this file is not runnable as-is.payplug/Oney/Bancontact/Scalapay/Apple Pay/American Express gateways — they keep using the existingPayPlugApiClientSDK flow untouched.Type of Change
Checklist
Code Quality
uhf_account_id/uhf_submerchant_external_idare intentionally hardcoded via env-var defaults for this QA phaseTesting
src/Upc/adapter and per new Capture/Notify/Status handler, covering the redirect vs. no-redirect branches, API exceptions, lock contention, and already-treated webhook replaySecurity & Ops
finally; Capture-time API errors fail the payment through the standard checkout error path