Releases: payplug/unified-plugin-core
Release list
1.1.3
π UPC 1.1.3
Adds the authorization / capture / cancellation payment lifecycle to UnifiedApiPaymentService, and
fixes payments failing for customers browsing over IPv6. No breaking changes; a plugin that upgrades
and changes nothing is unaffected.
π Fixes
- π IPv6 browser IP no longer rejected β the Unified API caps
browser.ipat 15
characters (an IPv4 maximum) and rejects an IPv6 address, so payments from IPv6 clients failed.
BrowserDto::toArray()now serializes anyipcontaining a:as0.0.0.0, for both
HostedFieldDtoandPaymentDto. The publicBrowserDto::$ipkeeps the caller's original value;
only the request payload changes.
β¨ Added
- π Authorization β no new call: it is
createPayment()withCommonFieldsDto::$capture = false,
optionally with the newpartialAuthorizationflag and/orauthorizationType
(AuthorizationType::PRE_AUTHORIZATION/FINAL_AUTHORIZATION).PaymentOutputnow also exposes
maxCaptureDateandremainingCapturableAmountfor an authorization-only creation (PRE-3670). - π³
UnifiedApiPaymentService::capturePayment()β full or partial capture, immediate or deferred,
one or several times, returning aCaptureOutput. - π«
UnifiedApiPaymentService::cancelPayment()β full (default) or partial cancellation of a
payment or authorization, returning aCancellationOutput. β οΈ 13 new exception subtypes so a plugin cancatchprecisely instead of parsing a generic
ApiException:- Business outcomes normalized from the Unified API response:
AuthorizationExpiredException,
PaymentAlreadyCapturedException,PaymentAlreadyCancelledException,
AmountExceedsAvailableException,OperationConflictException,
PartialCancellationNotAllowedException,PaymentNotVoidableException,
PaymentNotCapturableException,MultipleCaptureNotAllowedException. An issuer refusal reuses the
existingCardOperationException. - Local pre-call validation (no HTTP request sent):
InvalidCaptureRequestException/
CaptureAmountExceptionandInvalidCancellationRequestException/
CancellationAmountExceptionβ emptyorderId/description, non-positiveamount, or a missing
currencyalongside a partialamount.
- Business outcomes normalized from the Unified API response:
π Compatibility
Upgrading from 1.1.2 with no plugin-side change is a no-op: no existing signature, contract or
exception changed, and the new CommonFieldsDto properties default to null. Idempotency across
replayed capture/cancel requests remains the consuming plugin's job (IPaymentRepository / ILock);
UPC only normalizes the API's "already happened" signals into distinct exception types.
β Quality
PHPStan level 8, PHP-CS-Fixer, PHPUnit and CI verifying PHP 7.1β8.2 compatibility. New unit tests cover
every new class and the error-classification paths, plus two integration tests that assert the API's own
rejection against the shared staging fixture payment (proving auth/URL/JSON wiring without mutating it).
π Requirements
- PHP β₯7.4 to install/develop (build-tooling floor only β shipped source runs on PHP 7.1)
- Runtime dependency:
giggsey/libphonenumber-for-php
π Full Changelog: 1.1.2...1.1.3
1.1.3-rc0
What's Changed
- PRE-3670: Add authorization, capture and cancellation operation by @ilajili in #35
- PRE-3713: fall back to 0.0.0.0 for an IPv6 browser IP by @jhoaraupp in #37
Full Changelog: 1.1.2-rc0...1.1.3-rc0
1.1.2
π UPC 1.1.2
Additive release exposing the OpenID Connect id_token on TokenOutput, so a consuming plugin can
identify who authorized a connection β not just which account the resulting token authorizes.
No breaking changes; a plugin that upgrades and changes nothing is unaffected.
β¨ Added
- πͺͺ
TokenOutput::$idTokenβ theid_tokenreturned by an authorization-code exchange, the only
place a logged-in person's identity surfaces.GET /accountcarries no email (its payload is
id,company_ref,country,object,is_live,configuration,permissions,
payment_methodsand nothing else, verified live against several QA accounts), and the
client_credentialstoken authenticates a machine, so it names no user either. A consumer wanting
the merchant's address has to read theemailclaim off this token at login time. - π
OAuth2Client::requestToken()passesid_tokenthrough when the token-endpoint response
carries one, instead of discarding it while constructing theTokenOutput.
β»οΈ Changed
- Nothing behavioural.
idTokenis a trailing 4th constructor argument defaulting tonull, and
a purely additive public property β every pre-existing 3-argument caller and every read of
accessToken/expiresIn/tokenTypeis untouched. idTokenis deliberately optional and unvalidated:requestToken()is shared by
exchangeAuthorizationCode()andgetClientCredentialsToken(), and only the former can ever
produce anid_token, so asserting on it would reject a perfectly usable client-credentials
response. UPC does not parse the JWT β consumers decode whichever claim they need.
π Compatibility
Upgrading from 1.1.x with no plugin-side change is a no-op:
TokenOutputisfinal, so nothing can have subclassed it with a 3-argument constructor.- The new read is
isset($data['id_token']) && \is_string(...)β it cannot throw and adds no failure
path. - No cache invalidation needed.
TokenManagercaches only the bare access-token string, never
the object, so the token-cache format is unchanged. Nothing in UPC serializes,json_encodes or
get_object_vars()aTokenOutput. idTokenis non-null only when a caller usesexchangeAuthorizationCode()and passes a scope
containingopenid/email; theclient_credentialsgrant always yieldsnull.
TokenOutput for
debugging (print_r, var_dump, json_encode, or a logger that serializes context objects) will now
see an id_token in that output where nothing appeared before β and an id_token is a JWT carrying PII
(email, sub). No current consumer does this, and it affects only the authorization-code path.
β Quality
PHPStan level 8, PHP-CS-Fixer, PHPUnit, and CI verifying PHP 7.1β8.2 compatibility β all clean on this
release branch. Six unit tests added: three on the value object (assignment, the null default, and
one asserting the constructor explicitly does not validate idToken) and three on OAuth2Client
(surfaced from an authorization-code response, left null when absent, left null for
client_credentials). The last two guard the optionality β they fail the day someone makes id_token
required in the shared requestToken(). make verify-71 passes; the new nullable type hint is 7.1
syntax.
π¦ Consumers
- Sylius plugin PRE-3631 pins
^1.1.2β it displays the connected PayPlug account's email on the
gateway-configuration admin screen, which is impossible on 1.1.0/1.1.1.
π Requirements
- PHP β₯7.4 to install/develop (build-tooling floor only β shipped source runs on PHP 7.1)
- Runtime dependency:
giggsey/libphonenumber-for-php
π Full Changelog: 1.1.1...1.1.2
1.1.2-rc0
What's Changed
- PRE-3631: expose the OAuth id_token on TokenOutput by @adumont-payplug in #33
Full Changelog: 1.1.1...1.1.2-rc0
1.1.1
π UPC 1.1.1
Corrective release making submerchantExternalId optional across the payment-creation and refund
paths, and adding an optional currency to refunds β non-EUR configurations can now be paid and
refunded, which 1.1.0 made impossible.
π Fixed
- π¬
submerchantExternalIdis no longer required β it belongs to the UDV/MID configuration for
the payment's currency, not to a payment method: the EUR configurations own a submerchant,
the ones used for other currencies own none. 1.1.0 required it everywhere, so a non-EUR payment
or refund was rejected with400 "Invalid parameter."and had no way through.
CommonFieldsDto::__construct()'s 5th parameter is now?string = null, and
UnifiedApiPaymentService::createRefund()'s 5th is too β both still in place, so existing
5-argument positional callers are unaffected. - π³οΈ An empty submerchant is treated as "none" β
BuildsCommonPayloadBodyandcreateRefund()
omit the key entirely when it isnullor'', since a CMS reading an unset value out of
its own settings storage yields''far more often than a realnull, and''is rejected by
the API just like a submerchant the configuration does not own. When present, the key keeps its
original position in the body.
β¨ Added
- π±
currencyon refunds βUnifiedApiPaymentService::createRefund()takes an optional
trailing?string $currency = null, sent only when non-null and non-empty. Until now no currency
was sent at all, so$amount's minor units travelled bare for the platform to interpret β
unambiguous only while every payment used the account's default currency, and a real gap for a
multi-currency merchant.
β»οΈ Changed
createRefund()no longer rejects an emptysubmerchantExternalIdlocally with
InvalidRefundRequestExceptionβorderIdanddescriptionkeep that fail-fast guard.BuildsCommonPayloadBody::buildRedirectBody()extracted frombuildPayloadBody(); no behavior
change.- Design rationale in
CLAUDE.mdand.env.examplecorrected in place rather than deleted,
including the caveat that the staging run which confirmed the fix changed both
submerchantExternalIdandcurrencyat once.
β Quality
PHPStan level 8, PHP-CS-Fixer, PHPUnit, and CI verifying PHP 7.1β8.2 compatibility β all clean on
this release branch. Seven unit tests added or reworked around the new omission rules, plus the
refund integration test reworked to run on a configuration that owns no submerchant.
π Requirements
- PHP β₯7.4 to install/develop (build-tooling floor only β shipped source runs on PHP 7.1)
- Runtime dependency:
giggsey/libphonenumber-for-php
π Full Changelog: 1.1.0...1.2.0
1.1.0
π UPC 1.1.0
Feature release adding card-alias payments (create-and-store, or pay with a saved alias) alongside
the existing hosted-fields flow, plus full/partial refunds against the Unified API.
β¨ Added
- π³ Pay with a saved card alias β new
PaymentDto, sibling toHostedFieldDto, for paying
with an already-created alias (nohfToken). Both share onePaymentRequestPayloadcontract, so
UnifiedApiPaymentService::createPayment()accepts either. - π·οΈ Create an alias from a hosted-fields payment β
HostedFieldDto::$recurringMode+
paymentMethod.saveFutureUsagecreate an alias for future reuse;PaymentOutput::$aliasId
surfaces whichever alias was created or reused. - π Billing/shipping support β
BillingDto/ShippingDto, composingAddressDto/ContactDto/
ShippingScheduleDto, wired intoCommonFieldsDto. - β©οΈ Refunds β
UnifiedApiPaymentService::createRefund()for a full or partial refund of a
payment, validatingaccountId/orderId/description/submerchantExternalIdlocally before
any HTTP call.
π₯ Breaking
UnifiedApiHostedPaymentServiceis removed; its method now lives directly on
UnifiedApiPaymentService::createPayment().descriptionis now sent unconditionally in every payment-creation request instead of being
omitted when unset.
β Quality
PHPStan level 8, PHP-CS-Fixer, PHPUnit, and CI verifying PHP 7.1β8.2 compatibility β all clean on
this release branch.
π Requirements
- PHP β₯7.4 to install/develop (build-tooling floor only β shipped source runs on PHP 7.1)
- Runtime dependency:
giggsey/libphonenumber-for-php
π Full Changelog: 1.0.1...1.1.0
1.1.0-rc0
What's Changed
- PRE-3590: Add HF aliasing by @hdelaforce-payplug in #24
- PRE-3589: Fix UPC createRefund() β add required orderId/description/submerchantExternalId fields by @jhoaraupp in #29
New Contributors
- @hdelaforce-payplug made their first contribution in #24
Full Changelog: 1.0.1...1.1.0-rc0
1.0.1
π UPC 1.0.1
Patch release fixing a live notifier incident (PRE-3614) hit while diagnosing production webhook
handling, plus a Unified API operation-status polling fallback developed alongside it.
π Fixes
- π
execCode"0001"no longer misread as a final failure β PayPlug's notifier was
observed in production firing a webhook carryingexecCode "0001"("Authentification 3DSecure
requise", categorized "Acceptation", not an error) before the real, final notification for the
same operation. Under the old two-way mapping this was read as terminalFAILEDand, via
IPaymentRepository::isTreated(), permanently blocked the correct final notification from ever
applying.ExecCodeMapper::toPaymentOutcome()now maps"0001"to the existing
PaymentOutcome::THREE_DS_PENDINGinstead. - π Webhook signature verification fails open when no secret is configured β
WebhookNotificationHelper::verifySignature()previously rejected every notification
unconditionally, since no merchant/account currently has any way to configure a webhook secret.
It now accepts the notification unverified when no expectedAuthorizationheader value is set.
This is a deliberate, temporary tradeoff β any unauthenticated request to a plugin's webhook
endpoint is accepted today β pending a product decision on how webhook secret configuration will
be exposed; revisit once that lands.
β¨ Added
- π‘ Operation-status polling β
UnifiedApiPaymentService::getOperation()GETs the public
/processing-operations/operations/public/{id}endpoint, returning the same webhook-shaped
payloadWebhookNotificationHelper::parse()already reads β a fallback for a delayed or lost
webhook. A siblingUnifiedApiOperationService::getOperation()targets the private endpoint but
returns 403 for a merchant's own client credentials in staging; treat it as unverified until a
follow-up decides whether to keep or remove it.
β Quality
PHPStan level 8, PHP-CS-Fixer, PHPUnit, and CI verifying PHP 7.1β8.2 compatibility β all clean on
this release branch.
π Requirements
- PHP β₯7.4 to install/develop (build-tooling floor only β shipped source runs on PHP 7.1)
- Runtime dependency:
giggsey/libphonenumber-for-php
π Full Changelog: 1.0.0...1.0.1
1.0.1-rc0
What's Changed
- Feature/pre 3614 handling unified notifier by @adumont-payplug in #27
Full Changelog: 1.0.0...1.0.1-rc0
1.0.0
π UPC 1.0.0
This release adds asynchronous webhook/3DS confirmation, synchronous hosted-fields payment
creation, and completes the 3DS-pending redirect flow β the first release with a real end-to-end
payment path, from creation through the bank's 3DS challenge to the final webhook confirmation.
π₯ Breaking Changes
No CMS plugin has integrated against UPC yet, so these carry no runtime blast radius today.
- π¦
Models/βDataValues/+Output/β the category is gone;PaymentOutcome/
OperationDatamoved toDataValues/(durable state),TokenβTokenOutput,
AuthorizationRequestβAuthorizationRequestOutput, and the newHostedPaymentResultβ
HostedPaymentOutputmoved toOutput/(call-result value objects) - βοΈ
createHostedPayment()signature β replaced its original 11 positional parameters with
a single validatedHostedFieldDto
β¨ Highlights
- π Webhook / 3DS confirmation (PRE-3588) β
WebhookNotificationHelperverifies an inbound
"Payment Operation" notification'sAuthorizationheader (constant-time comparison) and parses
its body into anOperationData, mappingexecCodeto aPaymentOutcomevia the new
ExecCodeMapper; throws the newInvalidNotificationExceptionon a missing/invalid signature,
malformed body, or invalid field - π³ Hosted-fields payment creation (PRE-3587) β
UnifiedApiHostedPaymentService:: createHostedPayment()creates/confirms a payment from anhfTokenagainst the Unified API,
distinguishing a direct success from a pending 3DS/SCA redirect; newDto/(HostedFieldDto,
CommonFieldsDto,BrowserDto,CustomerDto) andValidators/(HostedFieldDtoValidator,
CommonFieldsDtoValidator) categories back the validated input - π Completing the 3DS-pending redirect (PRE-3551) β
CommonFieldsDtogainssuccessUrl/
cancelUrl(nested under aredirectobject, the merchant-return URLs after the bank's
challenge);HostedPaymentOutputgainsredirectHtmlalongsideredirectUrl, decoding the
Unified API's default Base64-encodedredirect.htmlself-submitting challenge form - π¬ Marketplace routing β
submerchantExternalIdadded as a requiredCommonFieldsDtofield
β Quality
216 tests / 517 assertions, PHPStan level 8, PHP-CS-Fixer, and CI verifying PHP 7.1β8.2
compatibility β all clean on this release branch.
π Requirements
- PHP β₯7.4 to install/develop (build-tooling floor only β shipped source runs on PHP 7.1)
- Runtime dependency:
giggsey/libphonenumber-for-php
π Full Changelog: 0.1.0...1.0.0