Skip to content

Releases: payplug/unified-plugin-core

1.1.3

Choose a tag to compare

@github-actions github-actions released this 01 Oct 14:44

πŸš€ 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.ip at 15
    characters (an IPv4 maximum) and rejects an IPv6 address, so payments from IPv6 clients failed.
    BrowserDto::toArray() now serializes any ip containing a : as 0.0.0.0, for both
    HostedFieldDto and PaymentDto. The public BrowserDto::$ip keeps the caller's original value;
    only the request payload changes.

✨ Added

  • πŸ” Authorization β€” no new call: it is createPayment() with CommonFieldsDto::$capture = false,
    optionally with the new partialAuthorization flag and/or authorizationType
    (AuthorizationType::PRE_AUTHORIZATION / FINAL_AUTHORIZATION). PaymentOutput now also exposes
    maxCaptureDate and remainingCapturableAmount for an authorization-only creation (PRE-3670).
  • πŸ’³ UnifiedApiPaymentService::capturePayment() β€” full or partial capture, immediate or deferred,
    one or several times, returning a CaptureOutput.
  • 🚫 UnifiedApiPaymentService::cancelPayment() β€” full (default) or partial cancellation of a
    payment or authorization, returning a CancellationOutput.
  • ⚠️ 13 new exception subtypes so a plugin can catch precisely 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
      existing CardOperationException.
    • Local pre-call validation (no HTTP request sent): InvalidCaptureRequestException /
      CaptureAmountException and InvalidCancellationRequestException /
      CancellationAmountException β€” empty orderId/description, non-positive amount, or a missing
      currency alongside a partial amount.

πŸ” 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

1.1.3-rc0 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 01 Oct 14:35
aa54946

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

Choose a tag to compare

@github-actions github-actions released this 15 Sep 14:25

πŸš€ 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 β€” the id_token returned by an authorization-code exchange, the only
    place a logged-in person's identity surfaces. GET /account carries no email (its payload is
    id, company_ref, country, object, is_live, configuration, permissions,
    payment_methods and nothing else, verified live against several QA accounts), and the
    client_credentials token authenticates a machine, so it names no user either. A consumer wanting
    the merchant's address has to read the email claim off this token at login time.
  • πŸ”“ OAuth2Client::requestToken() passes id_token through when the token-endpoint response
    carries one, instead of discarding it while constructing the TokenOutput.

♻️ Changed

  • Nothing behavioural. idToken is a trailing 4th constructor argument defaulting to null, and
    a purely additive public property β€” every pre-existing 3-argument caller and every read of
    accessToken / expiresIn / tokenType is untouched.
  • idToken is deliberately optional and unvalidated: requestToken() is shared by
    exchangeAuthorizationCode() and getClientCredentialsToken(), and only the former can ever
    produce an id_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:

  • TokenOutput is final, 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. TokenManager caches 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() a TokenOutput.
  • idToken is non-null only when a caller uses exchangeAuthorizationCode() and passes a scope
    containing openid/email; the client_credentials grant always yields null.

⚠️ One passive behaviour change worth knowing: a consumer that dumps a whole 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

1.1.2-rc0 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 15 Sep 14:07

What's Changed

Full Changelog: 1.1.1...1.1.2-rc0

1.1.1

Choose a tag to compare

@github-actions github-actions released this 07 Sep 13:09

πŸš€ 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

  • 🏬 submerchantExternalId is 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 with 400 "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" β€” BuildsCommonPayloadBody and createRefund()
    omit the key entirely when it is null or '', since a CMS reading an unset value out of
    its own settings storage yields '' far more often than a real null, 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

  • πŸ’± currency on 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 empty submerchantExternalId locally with
    InvalidRefundRequestException β€” orderId and description keep that fail-fast guard.
  • BuildsCommonPayloadBody::buildRedirectBody() extracted from buildPayloadBody(); no behavior
    change.
  • Design rationale in CLAUDE.md and .env.example corrected in place rather than deleted,
    including the caveat that the staging run which confirmed the fix changed both
    submerchantExternalId and currency at 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

Choose a tag to compare

@hdelaforce-payplug hdelaforce-payplug released this 28 Aug 14:18

πŸš€ 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 to HostedFieldDto, for paying
    with an already-created alias (no hfToken). Both share one PaymentRequestPayload contract, so
    UnifiedApiPaymentService::createPayment() accepts either.
  • 🏷️ Create an alias from a hosted-fields payment β€” HostedFieldDto::$recurringMode +
    paymentMethod.saveFutureUsage create an alias for future reuse; PaymentOutput::$aliasId
    surfaces whichever alias was created or reused.
  • 🏠 Billing/shipping support β€” BillingDto/ShippingDto, composing AddressDto/ContactDto/
    ShippingScheduleDto, wired into CommonFieldsDto.
  • ↩️ Refunds β€” UnifiedApiPaymentService::createRefund() for a full or partial refund of a
    payment, validating accountId/orderId/description/submerchantExternalId locally before
    any HTTP call.

πŸ’₯ Breaking

  • UnifiedApiHostedPaymentService is removed; its method now lives directly on
    UnifiedApiPaymentService::createPayment().
  • description is 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

1.1.0-rc0 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 28 Aug 14:05
ef4f04d

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

Full Changelog: 1.0.1...1.1.0-rc0

1.0.1

Choose a tag to compare

@github-actions github-actions released this 21 Aug 13:13

πŸš€ 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 carrying execCode "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 terminal FAILED and, via
    IPaymentRepository::isTreated(), permanently blocked the correct final notification from ever
    applying. ExecCodeMapper::toPaymentOutcome() now maps "0001" to the existing
    PaymentOutcome::THREE_DS_PENDING instead.
  • πŸ”“ 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 expected Authorization header 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
    payload WebhookNotificationHelper::parse() already reads β€” a fallback for a delayed or lost
    webhook. A sibling UnifiedApiOperationService::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

1.0.1-rc0 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 21 Aug 13:09
7ccf844

What's Changed

Full Changelog: 1.0.0...1.0.1-rc0

1.0.0

Choose a tag to compare

@github-actions github-actions released this 17 Aug 09:13

πŸš€ 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/
    OperationData moved to DataValues/ (durable state), Token→TokenOutput,
    AuthorizationRequest→AuthorizationRequestOutput, and the new HostedPaymentResult→
    HostedPaymentOutput moved to Output/ (call-result value objects)
  • ✍️ createHostedPayment() signature β€” replaced its original 11 positional parameters with
    a single validated HostedFieldDto

✨ Highlights

  • πŸ”” Webhook / 3DS confirmation (PRE-3588) β€” WebhookNotificationHelper verifies an inbound
    "Payment Operation" notification's Authorization header (constant-time comparison) and parses
    its body into an OperationData, mapping execCode to a PaymentOutcome via the new
    ExecCodeMapper; throws the new InvalidNotificationException on a missing/invalid signature,
    malformed body, or invalid field
  • πŸ’³ Hosted-fields payment creation (PRE-3587) β€” UnifiedApiHostedPaymentService:: createHostedPayment() creates/confirms a payment from an hfToken against the Unified API,
    distinguishing a direct success from a pending 3DS/SCA redirect; new Dto/ (HostedFieldDto,
    CommonFieldsDto, BrowserDto, CustomerDto) and Validators/ (HostedFieldDtoValidator,
    CommonFieldsDtoValidator) categories back the validated input
  • πŸ” Completing the 3DS-pending redirect (PRE-3551) β€” CommonFieldsDto gains successUrl/
    cancelUrl (nested under a redirect object, the merchant-return URLs after the bank's
    challenge); HostedPaymentOutput gains redirectHtml alongside redirectUrl, decoding the
    Unified API's default Base64-encoded redirect.html self-submitting challenge form
  • 🏬 Marketplace routing β€” submerchantExternalId added as a required CommonFieldsDto field

βœ… 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