Download the PHP package xpiston/worldline-sips without Composer
On this page you can find all versions of the php package xpiston/worldline-sips. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download xpiston/worldline-sips
More information about xpiston/worldline-sips
Files in xpiston/worldline-sips
Package worldline-sips
Short Description Worldline Sips 2.0 (Sogenactif, Mercanet, Scellius, Sherlock's) for PHP, Laravel and Symfony: sealed Paypage requests, verified responses, capture, cancel and refund.
License MIT
Homepage https://github.com/xPiston/worldline-sips
Informations about the package worldline-sips
Worldline Sips is the payment platform behind a great many French bank brands: Sogenactif (Sociรฉtรฉ Gรฉnรฉrale), Mercanet (BNP Paribas), Scellius (La Banque Postale), Sherlock's (LCL), ElysNet (HSBC). One protocol, one contract shape, one set of seal rules โ and a different name on every merchant agreement.
This package implements that protocol: the sealed form that sends a customer to the payment page, the two responses that come back, and the three cash-management calls that move the money afterwards. Nothing else. It has no opinion about your orders and touches the network only when you ask it to capture, cancel or refund.
Everything below was checked against Worldline's own servers. The simulation platform's credentials are published in its quick start guide, and the Office test platform's in the Office guide โ so this package has a live test suite that needs no contract, and every claim in this README that could be wrong was measured rather than read. The places where the documentation contradicts itself are called out, with what the server actually does.
โจ What's inside
- ๐ Both seal algorithms โ
HMAC-SHA256over the Data element, and the plainSHA256(Data + key)that is still the platform's default and that its own documentation advises against - ๐ The order rule, enforced by design โ a response is verified against the string that arrived, never one rebuilt from its fields
- ๐งพ Paypage requests as hidden inputs or a whole
<form>, escaped after sealing and never before - ๐ฌ Both response channels โ the browser's return and the server-to-server notification, same fields, same seal, different meaning
- ๐ณ Capture, cancel and refund, with the field set the platform actually accepts and a response seal that is checked
- ๐งฎ Money as integers and a numeric currency code โ
amount=2500,currencyCode=978, and the exponent that says which one means what - ๐ท๏ธ
transactionReferenceoptional, because a shop can be configured to let Sips generate it โ and then sending one is an error - ๐งฉ Laravel and Symfony bridges, a hundred lines each, both optional
- โ 243 tests with no network, plus 10 live tests against Worldline
๐ Quick start
No contract yet? The simulation platform is open, and its credentials are published:
That form posts three fields โ Data, InterfaceVersion, Seal โ and the
simulator answers with a real payment page. Its test cards are deterministic:
the result is decided by the last two digits of the card number, and the 3-D
Secure step is simulated so you choose the outcome.
For production, the three values Worldline issued:
Reading the response
Sips posts the result twice: to normalReturnUrl, which the customer's
browser carries, and to automaticResponseUrl, server to server. Same
fields, same seal. The documentation is blunt about the difference and so is
this README: a customer can close the tab, so business logic belongs on the
second one and the first is for showing them a page.
matches() is three comparisons โ reference, amount, merchant id โ and it is
not optional thinking. A valid seal proves the message is authentic and
unaltered. It does not prove it is about the order you are holding.
Capturing, cancelling, refunding
These three call Worldline over HTTPS, so they need a PSR-18 client โ pass
one, or let php-http/discovery find
the one your application already has:
Rendering a form and verifying a response need no HTTP client at all, which is why the constructor does not ask for one.
๐ The seal, in one page
Every message in both directions carries one, and Sips has three different rules for building the string it is computed over. Not two algorithms in one shape โ three shapes.
Paypage concatenates name=value pairs with a |:
Office concatenates the values only, sorted by field name, and leaves
out keyVersion and sealAlgorithm:
And on top of that, two algorithms. HMAC-SHA256(Data, key), or
SHA256(Data + key) โ a plain hash with the key glued on the end, which is the
platform's default and which its own documentation calls "no longer
recommended". Getting the better one takes one field: sealAlgorithm.
The rule that decides the design
"It is important not to sort the content of the Data field to calculate the Seal of the response. Keep the fields in the sequence you received them."
A Sips response has around a hundred fields in an order only the platform
knows โ the documented example has 104, starting at captureDay and ending at
abortedProcessingLocation. Sort them, drop one you do not recognise, or
re-encode a value, and every payment reads as a forgery.
So this package never rebuilds a response. DataElement::received() keeps the
string that arrived and parses it only for reading; the seal is computed over
transmitted(). The same class builds an outgoing message once, and that one
string is both what is sealed and what is posted. There is no path through an
array and back.
What was measured
Both documented vectors reproduce exactly โ ac2332b5โฆ for a SHA-256 request,
c946655cโฆ for an HMAC response of 104 fields. Then, against the live Office
test platform, one cancel request sent six ways:
| the seal was computedโฆ | the platform answered |
|---|---|
over the values, sorted by name, without keyVersion and sealAlgorithm |
25 โ transaction not found: the request was read |
over name=value\|โฆ instead, the Paypage way |
34 |
over the values including keyVersion |
34 |
| with the right shape and the wrong key | 34 |
as SHA256(data+key) while declaring HMAC |
34 |
as sixty-four fs |
34 |
Five negative controls and one positive, which is a stronger statement than any
documentation. The response seal was confirmed the same way: a reply of
{"responseCode":"12"} is sealed as the HMAC of the string 12.
What a payment actually looks like
Driven end to end through a browser against the simulator โ the form, a card typed on Worldline's page, the 3-D Secure step, and the response verified by this package:
| the customerโฆ | responseCode |
what came back |
|---|---|---|
| pays with a good card and passes 3-D Secure | 00 | 26 fields: brand, masked PAN, authorisation number, 3D_SUCCESS |
| is refused, then gives up | 17 | 18 fields, no brand, no PAN |
| fails 3-D Secure, then gives up | 17 | 18 fields |
Two things in that table are worth more than the table.
A refusal does not end the payment. A refused card puts the customer back
on Worldline's payment-method page to try another one; nothing is posted to
your URLs. The response leaves only when the payment ends โ paid, given up,
or out of attempts. So a shop sees far more 17 than 05, and an order with
no response at all is a customer who closed the tab. Which is the argument for
automaticResponseUrl restated as an observation: the browser is not a
reliable messenger.
The field count changes with the outcome. Twenty-six fields on an accepted
payment, eighteen on an abandoned one โ no paymentMeanBrand, no maskedPan,
no authorisationId. Every accessor on PaymentResponse returns null rather
than assuming, and this is why.
๐งฐ Laravel
The provider is discovered automatically.
Leave the credentials empty and it falls back to the published simulation shop,
so the package boots five minutes after composer require. On production it
refuses to boot without them, which is the one place where an exception is
kinder than a default.
ResponseRequest::fields() is not decoration. $request->all() merges the
query string into the body โ and a return URL with a query parameter is the
normal shape here, because normalReturnUrl is where a shop puts its own
order id. One ?order=42 and the seal is computed over a field Sips never
sent.
php artisan vendor:publish --tag=sips-config writes the config file, with the
reasoning for each option in it.
๐งฐ Symfony
Sips and ResponseVerifier are then autowired, and the PSR-18 client comes
from the container as soon as symfony/http-client is installed. The
notification route has to be reachable without a session and without
authentication โ if a firewall covers the path, give it security: false.
๐งฑ How it's built
Decisions visible in the code
- A response is verified against the string that arrived. The single most
important line in the package, and the reason
DataElementexists. - Nothing in
PaymentResponsethrows. The server-to-server channel is a POST Sips expects to succeed; an unfamiliar brand, an unreadable date or a response code added last quarter each yieldnullfrom their own accessor and change nothing else. - An empty
responseCodeis not a payment. Said out loud because the first draft of this package padded it to two characters,00is an acceptance, and a response with no code at all read as accepted. There is a test named after it. - An unsealed
00from an Office call is not done.isDone()wants both an accepted code and a verified seal: Sips seals its answers, so an answer that cannot be verified came from something else. - Money is an integer and a currency.
2500is twenty-five euros and two thousand five hundred yen; the exponent is what says which. A currency whose numeric code this library does not know is refused by name rather than guessed at two decimals. transactionReferenceis optional. A shop can be set up to let Sips generate it, and then sending one answerscode=12 Merchant provided transaction key, but is configured for auto generated transaction key- which is how this was found, on the simulator, in the first five minutes.- The field set of an Office call is exactly what the platform accepts.
commentis refused.operationOriginalReferenceis refused. Both were invented here first and both came back as12witherrorFieldNamenaming them. The platform is a better reviewer than a documentation. CR_WS_for cash management, notIR_WS_. Sending a card-order interface version to a refund answers12 errorFieldName: interfaceVersion.- The key travels with its version. Rotating a key on the Sips Download
extranet without bumping
keyVersionis the documented road to answer code 34 on every payment, so the two are one object. - A key with a trailing newline is refused, not trimmed. It is the single most likely thing to be wrong about a key pasted out of an extranet, and trimming it silently would hide the day somebody pastes two.
- Escaping happens after sealing, once, in
PaymentForm. - The platforms are three, and the hostnames are two per platform. The
payment page is on
payment-webinit.*, the Office services onoffice-server.*. An integration that guesses the second from the first finds out on the day of its first refund.
โ Tests
The default suite needs nothing but PHP 8.2 and Composer, and runs in under half a second.
The live suite is separate on purpose: it talks to somebody else's server, so a red run there means "Worldline is down" as often as "this is broken", and that is not what a merge gate is for. It runs nightly in CI and by hand, and it needs no credentials. What it proves cannot be proved any other way โ that the seal, the field names and the interface versions are what the platform expects, and that a wrong key is refused, which is the control that makes the rest mean something.
Three groups in the default suite are worth naming:
Against the documentation. Worldline publishes a Data string, a key and the seal it expects, for both algorithms. Those are in the suite verbatim, together with a test that sorting the documented 104-field response breaks its seal.
Against a fake platform. tests/Support/Platform seals a response the way
Sips does, so the pair that means something can be asserted: a response sealed
correctly is accepted, and the same response with its fields reordered is not.
Against the real frameworks. The Laravel bridge runs in a Laravel application booted by Testbench; the Symfony bundle compiles a real Symfony container. The second earned its place immediately โ it caught two service definitions that would have broken every installation, one of them an object where Symfony only accepts scalars, enums and services.
๐ค Why another package?
Worldline publishes its own PHP SDK. Measured on Packagist rather than guessed at:
worldline/sips-payment-sdk |
last stable 1.0.7, March 2021, marked abandoned, php >=7.0 |
| downloads on that dead package | ~99,000 |
kptive/payment-sips-bundle |
last stable January 2015 |
ambroisemaupate/omnipay-sips2 |
last stable January 2018 |
So the official connector for a platform that serves five bank brands has been abandoned since 2021, and the community alternatives predate PHP 8 entirely. That is the gap. Beyond the dates: the three seal shapes, the order rule, the two response channels, the field sets that only the platform will tell you, and framework wiring that does not quietly put a query parameter into a signature โ those are where the afternoons go, and they are what this package is.
โ ๏ธ Not included
duplicateโ charging a stored card again. Its field set could not be established: the platform answers12to every shape tried and, unlike its answers for the three implemented operations, names no field. A payment library should not ship a guess about which fields move money.- Sips In-App, Walletpage and the SOAP interfaces. Different connectors with their own guides.
cardOrderand the other direct-card Office services, which mean holding card numbers and a PCI DSS scope most shops should not want.- The summary file and the 3-D Secure emulation service.
- No order model, no persistence, no events, no retries. The results say what is worth retrying; your application's own tooling does the rest.
- SHA-1 is nowhere near this protocol, and the SHA-256 concatenation that is remains supported because merchants are on it โ with a docblock saying why HMAC is the better shape.
๐ The documentation this was built from
- Sips Paypage POST โ the fields, the seal, the response, and the two test vectors.
- Quick start guide โ the simulation credentials and the test cards.
- Sips Office JSON โ the cash-management services and their own seal rule.
- RFC 2104 (HMAC).
Where the documentation contradicts itself, the code says so and a test pins
the behaviour: the default algorithm is documented as SHA-256 on the Paypage
POST page and as HMAC on the JSON one (this package declares it explicitly
either way), and sealAlgorithm is spelled HMAC-SHA-256 in a request and
sha256 in a documented response (both are read).
License
MIT.
All versions of worldline-sips with dependencies
ext-hash Version *
ext-json Version *
psr/http-client Version ^1.0
psr/http-factory Version ^1.0
psr/http-message Version ^1.1 || ^2.0