Download the PHP package sparkcrm/spark-sdk without Composer

On this page you can find all versions of the php package sparkcrm/spark-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 spark-sdk

SparkCRM Checkout SDK (PHP)

A standalone PHP SDK for the SparkCRM DTC Checkout API. It takes you from init() → lead → order → upsell → complete, plus tax quotes, decline salvage, refunds, and the PayPal redirect/capture flow — without you ever touching the raw HTTP API.

The base URL is fixed to https://api.sparkcrm.io.


Install

With Composer (recommended)

The SDK speaks HTTP through any PSR-18 client. Unless your app already ships one, you must also install an implementation + PSR-7 factories — they are then auto-discovered (the first real API call fails without them):

Download a ready-to-use bundle (no Composer)

Don't use Composer? Each GitHub Release ships a spark-sdk-<version>.zip with everything already included — the SDK, its dependencies, and a working HTTP client. Download it, unzip it into your project, and include the bundled autoloader:

That's it — no Composer, no extra HTTP-client install.

From source without Packagist

Want to vendor a specific copy, or it isn't on Packagist yet? Point Composer at the repo, or clone it and let Composer build the autoloader:

Dropping src/ in on its own is not enough: the SDK needs a few PSR packages (psr/http-*, php-http/discovery, psr/log, psr/simple-cache) plus a PSR-18 client at runtime. The downloadable bundle and both Composer paths above all bring those along for you.


Before you start — read this once

Four things silently break a first integration. Get these right and the rest just works:


Quick start

A checkout spans two separate HTTP requests — the landing-page load and the checkout submit. Build a fresh client in each; state carries over via the store.

Need a live tax quote before charging? See Tax, subscriptions, refunds, search — it is never auto-called.


init() — call once on page load

init() does two things and stores the result for the rest of the funnel:

Everything captured here is auto-merged into createLead() / createOrder() so you never re-pass ip/user_agent/utm/affiliate.

campaign is an integer and is required. currency defaults to USD.

Browser requests

Pass the incoming PSR-7 ServerRequest so the SDK can read the query string, client IP and User-Agent. If you don't pass one, it falls back to $_GET / $_SERVER.

Behind a proxy or load balancer

Out of the box the SDK reads the real customer IP from the forwarded headers (X-Forwarded-ForX-Real-IPCF-Connecting-IP), falling back to REMOTE_ADDR for direct connections — so a checkout behind a CDN or load balancer records the customer's IP, not the proxy's, with no configuration.

To harden against header spoofing in a known topology, list your trusted proxies explicitly; forwarded headers are then honored only when the connection comes from one of them, and the client is the right-most non-proxy hop:

Pure server-to-server

Callers with no browser request can pass ip_address / user_agent / affiliate explicitly; auto-capture is then skipped.


Choosing a state store

The SDK remembers the buyer context + session_id and the live order_number / transaction_number between requests. Pick the store that matches your topology:

Store Use when Notes
SessionStore (default) single server the session cookie is the correlation key
CacheStore (PSR-16) multi-server / load-balanced keyed by a small opaque cookie (e.g. spark_ref); any node recalls state
SignedCookieStore stateless nodes, no session/cache authenticated-encrypted cookie (libsodium, requires ext-sodium); contents are confidential and tamper-evident
ArrayStore pure server-to-server, one process does not survive across requests
LayeredStore want a fallback chain e.g. session → cache → signed cookie

PayPal note: the correlation cookie is set Secure; HttpOnly; SameSite=Lax. Lax (not Strict) is required so the cookie is still sent when the customer returns from the PayPal redirect (a cross-site top-level navigation).

With the default SessionStore, the correlation key is the PHP session cookie. The SDK hardens it to Secure; HttpOnly; SameSite=Lax on construction (a no-op once the session has started — construct the client before session_start()). For local HTTP development, disable the Secure flag:


createOrder() — one smart method

createOrder() picks the endpoint based on SDK state:

The full-order path additionally requires customer.first_name + customer.last_name and attaches ip / user_agent / utm / affiliate / products / campaign to the call. After it returns, the state is identical for both paths, so processUpsell() / completeOrder() are unchanged.

Need to force a full order even when a lead exists? Use createFullOrder().

Reading the result

A decline is a result, not an exception:


Decline salvage

When the per-decline retry cap is reached (max_reprocess_attempts_per_decline / max_upsell_declines), the API returns a declined result, not an exception — ->isDeclined() is true and ->isCapped() tells you the cap (rather than the gateway) ended the retries, so you can stop retrying and message the customer.

reprocessPayment() charges the order already in the store and injects that order_number for you — but it does not check one exists. If the funnel state was lost (a fresh client with no resume() / init()), it sends a null order and the API rejects the call. Run it on the same funnel where createOrder() ran.


PayPal (redirect + capture)

The initial PayPal order is a two-phase flow; upsells are seamless.

If the customer cancels instead, they come back to your cancel_url — the SDK appends ?spark_canceled=1 to it so you can detect and clean up the abandoned order. The order is simply left uncaptured.

How spark_order is appended

A PayPal return_url can only carry ?spark_order=<order> if the order exists before the redirect. createOrder() guarantees that:

On top of the URL marker, the persistent store also carries the order across the redirect via the first-party SameSite=Lax cookie, so resume() recovers it even if the marker is lost.

createFullOrder() is the explicit escape hatch that always hits /checkout/orders and never creates a lead; it therefore does not append spark_order. Use it only when you don't need redirect-marker survival.


Tax, subscriptions, refunds, search


Param normalization

init() reads every query param off the landing-page URL and maps it to what Spark accepts. Matching is case-insensitive and ignores separators (- _ space), so affId, aff_id, AFFID, Aff-Id all resolve to the same target.

Extend the alias map for edge cases via Config:

Card expiry is normalized automatically: 12/26, 12/2026, or split card_exp_month + card_exp_year all become a canonical card_exp of MM/YYYY (set Config(splitCardExpiry: true) to emit the split fields instead). Malformed input — an unparseable card_exp, a month outside 0112, or a year that isn't 2 or 4 digits — throws ValidationException locally, before any request is sent (not a server-side 422, not a declined result).

payment.method is validated against the supported set (card, onfile, external, paypal_wallet); an unknown method throws a ValidationException before any request is sent.


Errors: results vs exceptions

Outcome How it surfaces
Decline (gateway or pre-gateway) OrderResult::isDeclined()
Duplicate order OrderResult::isDuplicate()
PayPal redirect needed OrderResult::needsRedirect()
Upsell reprocess already running (409) OrderResult::inProgress()
401 invalid/missing token AuthException
403 token lacks ability PermissionException
404 not found NotFoundException
422 field validation (has an errors map) ValidationException (->errors())
422 processing failure (no field errors, e.g. a failed capture/refund) a non-approved OrderResult — inspect ->message() (not thrown)
429 rate limited (after retries) RateLimitException (->retryAfterMs())
402 account past due AccountSuspendedException
5xx / transport (after retries) ApiException

All exceptions extend SparkCheckout\Exception\SparkException and expose ->httpStatus() and ->raw(). Requests are retried with exponential backoff on 429 and 5xx (honoring the X-RateLimit-Reset delay).


Test mode

Enable test mode to tag every charge/session request as a test. Whether a tagged request actually routes to a sandbox gateway is governed server-side by the campaign's gateway configuration — the SDK only marks the request.


Laravel (optional)

The package ships an optional Laravel bridge. The service provider is auto-discovered — no manual registration needed. Set your token and (optionally) publish the config:

Resolve the client from the container or use the Spark facade. The store is backed by the Laravel cache, keyed per visitor (the session id when a session is started, otherwise an opaque spark_ref cookie); bindings are scoped, so they reset cleanly per request under Octane.

The Laravel bridge requires illuminate/contracts (already present in any Laravel app); the core SDK has no framework dependency.


License

MIT.


All versions of spark-sdk with dependencies

PHP Build Version
Package Version
Requires php Version ^8.1
ext-sodium Version *
psr/http-client Version ^1.0
psr/http-factory Version ^1.0
psr/http-message Version ^1.1 || ^2.0
php-http/discovery Version ^1.19
psr/log Version ^1.1 || ^2.0 || ^3.0
psr/simple-cache Version ^2.0 || ^3.0
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 sparkcrm/spark-sdk contains the following files

Loading the files please wait ...