Download the PHP package dominaite/merchant-sdk without Composer

On this page you can find all versions of the php package dominaite/merchant-sdk. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package merchant-sdk

dominaite/dominaite-php

Server-side PHP client for the Dominaite merchant API. One call from your backend opens a hosted checkout session; a two-line script tag renders the payment widget on your page. Card details go straight from your customer's browser into the payment widget - they never touch your server, which keeps your PCI scope minimal (SAQ A).

Works on plain PHP 7.4+ with curl. No framework required.

The integration is three moving parts: create a session from your backend, render the widget, and receive a webhook when the payment lands. Webhooks are how you learn the outcome; polling is the fallback for when you have not set one up yet.

Install

PHP 7.4 or newer with ext-curl, ext-json and ext-mbstring. No other dependencies.

Credentials

You get two values from Dominaite (shown once - store them like passwords):

Every request is signed with the secret (HMAC-SHA256) and timestamped. Keep your server clock on NTP - signatures older than 5 minutes are rejected.

The third constructor argument overrides the base URL for non-production environments. It has to be https://: the key id and the signature travel in headers, and a captured signed request stays replayable for the whole 5 minute clock window. http:// is accepted only for localhost, 127.0.0.1 and ::1, so a local mock still works. Anything else throws InvalidArgumentException at construction rather than on your first live payment.

Do not dump the client

The client holds your secret in memory. Do not var_dump(), print_r(), var_export(), (array)-cast or serialize() a client as a way to inspect it, and do not let one reach a log line or an error tracker.

var_dump(), print_r() and serialize() are redacted for you - the client implements __debugInfo() and __serialize(), which Symfony VarDumper, Ignition and Whoops honour too, so a client caught in an exception page does not print your secret. json_encode() returns {} because the properties are private.

var_export(), an (array) cast and Reflection are NOT redacted. They honour no hook and print the secret in full. There is no way for the SDK to intercept them, so this one is on you.

One more thing worth setting in production php.ini:

Without it, a stack trace records the arguments each frame was called with, so an exception thrown anywhere below new DominaiteClient(...) carries your plaintext secret into whatever renders or ships that trace.

Ping before your first session

One signed GET that creates nothing, so anything that fails here is your credentials, your signing or your clock - not the payment:

Watch clockSkewSeconds: the gateway rejects requests once it passes 300, so a number that keeps growing is your cue to fix NTP before payments start failing.

Create a session (your create_session.php)

orderReference is limited to 100 characters, counted as characters and not as bytes - a 100 character Cyrillic or Greek reference is 200 bytes and is fine. Emoji and rarer CJK characters count double on the server, so stay a couple of characters clear of the limit if your references contain them; the server has the final say either way.

That's the checkout half: the session call above, the script tag, and your domain bound to your checkout by Dominaite during onboarding. The other half is the webhook that tells you the payment happened.

Webhooks

Register an endpoint in the dashboard (Developers, Webhooks): an HTTPS URL, the events you want, and you get a signing secret whsec_... shown exactly once. Store it like the API secret. Regenerating it kills the old one.

Receiving a delivery (your webhook.php)

verifyWebhook($payload, $signatureHeader, $secret, $toleranceSeconds = 300, $now = null) returns true or false. It returns false for everything the sender controls: a bad signature, a body that changed by one byte, a stale timestamp, a missing or garbled header. It throws InvalidArgumentException only for your own mistakes, an empty secret or a negative tolerance. Verify before you parse, always.

The header is X-Webhook-Signature: t=<unix seconds>,v1=<hex>, where v1 is HMAC-SHA256 over "{t}.{raw body}" keyed with your endpoint secret. The timestamp is inside the MAC and checked against a 300 second window, so a captured delivery cannot be replayed later.

What arrives

Flat envelope, no success wrapper to branch on. Amounts are minor units: amount is what you get paid, grossAmount is what moved on the card, surchargeAmount is the difference when a surcharge applies.

Events: payment.succeeded, payment.failed, payment.requires_capture, payment.cancelled, payment.abandoned, payment.refunded, payment.disputed. payment.succeeded is the only one that means money in hand. In-flight states (pending, processing) are not webhooked, so drive that part of your UX from the session status.

Delivery guarantees

Reconcile anyway

Webhooks complement a reconciliation sweep, they do not replace it. Keep a job that walks your open orders and calls getStatus() on anything past its expected settle time. There are windows where a delivery never lands: a chain parked on a disabled endpoint, or an outage on our side between the payment and the publish. The sweep is what closes them, and it is not optional if you care about your books.

Amounts are minor units

amount is always an integer in the currency's minor unit: 2500 is 25.00 EUR, 1000 is 10.00 JPY-equivalent in a two-decimal currency. The amount is locked server-side - what you pass here is what gets charged; nothing in the browser can change it.

Retries and double-charges

Every createCheckoutSession call carries an idempotency key (auto-generated, or pass your own as idempotencyKey). Retrying with the same key never opens a second payment - on a timeout, retry with the same key rather than generating a new one. When you let the SDK generate the key, read it back with getLastIdempotencyKey() so the retry can reuse it:

A replay does not hand you the original session back. If the first attempt did reach the gateway, the retry answers HTTP 200 with success=false and a replay code - DUPLICATE_REQUEST, ALREADY_PROCESSED, PRIOR_ATTEMPT_FAILED or IDEMPOTENCY_KEY_REUSED - which the SDK raises as a CheckoutRefusedException. The original session's cashierKey and cashierToken are not in that response, so a retry cannot be your only path to rendering the widget. What the refusal gives you is the transaction id to reconcile against; see "Recovering from a replay refusal" below. Store transactionId and the idempotency key when a create succeeds, and treat the replay refusal as "go look up what the first attempt did", not as an error to show the payer.

Rate limits

60 requests per minute per API key, 120 per minute per source IP. The per-IP bucket is the one that surprises people: several keys behind one egress address share it, so a key well under 60/min can still be limited.

Over the limit the API answers HTTP 429 and the SDK raises RateLimitException. It is not retried for you - a loop against a rate limiter just spends the next window too, and on createCheckoutSession() retrying is a decision about a payment that belongs in your code. getRetryAfterSeconds() gives the server's own number of seconds to wait, or null when it did not send a usable one; treat null as "use your own backoff", not "retry now". When you do retry, reuse the idempotency key.

RateLimitException extends ApiException, so code written before it existed still catches a 429. Catch RateLimitException first if you want to branch on it.

The polling loop in "Fallback: status polling" is the usual way to hit this. Poll one transaction every few seconds, not every transaction every second.

Response size

Responses are read up to 10 MB and no further. A real merchant-API response is a few kilobytes, so anything near that is a captive portal or an edge serving something that is not us, and reading it to the end would grow a worker's memory for no reason. An oversized response surfaces as TransportException - retryable, same as any other transport failure.

Sessions expire

A session is valid for 2 hours. If the payer comes back later, create a new session - and re-POST with the same order-derived idempotency key, not a fresh one: from a few minutes past expiry the same key answers with a fresh session (see "Recovering from a replay refusal").

Stored payment methods (recurring)

Pass 'saveCard' => true when you create a session and, once that payment is approved, the gateway keeps the card on file. You never see the card number or the provider token: getStatus() returns a storedPaymentMethod with an opaque id (pm_ + 32 hex characters), the brand, the last4 and the expiry, and that id is what you charge and revoke with. Store it against your customer. (paymentMethod on the same status is something else: the gateway's string category of how the payer paid, card, wallet and so on.)

A charge is signed exactly like a session and carries an Idempotency-Key, so a retry after a timeout with the same key never charges the card twice: the gateway replays its first answer, HTTP status included. getLastIdempotencyKey() reads the key back the same way it does for a session. The HTTP status is the contract on this route: 201 (or 200 on a replay) returns the charge, 402 returns the charge too (status failed plus declineClass), and 409, 422, 502 and 503 throw ChargeException with getErrorCode(), getHttpStatus(), the gateway's message and, when the gateway attached the charge row, getCharge() and getTransactionId(). Only authentication (401/403), an id that is not yours (404, ApiException with getErrorCode() PAYMENT_METHOD_NOT_FOUND), validation (400, ApiException), rate limiting (429) and network failures keep their generic exceptions. declineClass and declineCode are null unless the charge was declined; the gateway omits them on the wire and the SDK reads absent as null.

Revoking signs an empty key and an empty body, like getStatus(). A revoke that fails with RevokeException changed nothing: MERCHANT_API_UNAVAILABLE (503) is retryable, UPSTREAM_CONTRACT_ERROR (502) is not. After a revoke the status read keeps the storedPaymentMethod with status revoked, and a charge against it is refused with PAYMENT_METHOD_NOT_ACTIVE. An id that is not yours is an ApiException with getHttpStatus() 404.

Fallback: status polling

Use this when you have not registered a webhook endpoint yet, inside your reconciliation sweep, or any time you need the current state of one specific payment on demand.

status is one of: pending, processing, succeeded, failed, refunded, partially_refunded, cancelled, disputed, requires_capture, abandoned. While the session is still payable the response also carries expiresAt; after that instant a pending session can only become abandoned. An unknown transaction id throws an ApiException with HTTP 404.

succeeded is the only value that means the payment is complete. Keep polling on pending, processing and requires_capture - none of them is terminal.

requires_capture is not "unpaid": the payer has already paid and the funds are held awaiting capture. Never treat it as an abandoned order.

Treat any status you do not recognise as still-open as well: a value the API adds later should make you keep polling, never silently close an order that is still live.

Poll after the payer returns to you, or on your order timeout - not in a tight loop; the endpoint is rate limited per key.

Recovering from a replay refusal

When your idempotency key collides with an earlier attempt, the refusal names the transaction it collided with, so you can reconcile instead of minting a second payment:

getTransactionId() is null when the API did not name one (a concurrent-race DUPLICATE_REQUEST knows the key is taken but not yet by which row), so check it before use. DUPLICATE_REQUEST means a session for this key is open, or expired within the last few minutes: re-POST the same key shortly, never a fresh one. The full refusal payload is on getResult().

One replay is not a refusal at all. A session that expired unpaid is superseded: from a few minutes past its expiry, re-POSTing the same key returns an ordinary success with a fresh session (new transaction id, same key), so a customer who comes back late just pays. Keep the order-derived key for the life of the order to keep that path open. The band is not endless - once the platform has independently closed the attempt (about an hour past expiry), the replay answers PRIOR_ATTEMPT_FAILED and the key is spent; reconcile and use a fresh key.


All versions of merchant-sdk with dependencies

PHP Build Version
Package Version
Requires php Version >=7.4
ext-curl Version *
ext-json Version *
ext-mbstring Version *
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package dominaite/merchant-sdk contains the following files

Loading the files please wait ...