Download the PHP package wbreeze/sol-pay-client without Composer

On this page you can find all versions of the php package wbreeze/sol-pay-client. 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 sol-pay-client

sol-pay-client (PHP)

Prologue

This file is read in two repositories, and it is the same file.

Find package development in the php-client/ directory of the repository, wbreeze/sol-pay. Find a distribution only mirror at, wbreeze/sol-pay-php. Packagist publishes from the mirror. The mirror, wbreeze/sol-pay-php, is a projection, never a place to work. The script, bin/split-php-client in the wbreeze/sol-pay repository uses git split to synthesize history from this php-client directory. Any commit made within wbreeze/sol-pay-php would prevent the next split from fast-forwarding and will be erased, because the synthesized tree is the php-client directory contents, nothing else.

In short, make any and all changes in the php-client directory of the wbreeze/sol-pay repository.

See "Publishing" below.

Description

A server-side PHP port of wasm-client's core, for a site whose server has no Rust toolchain and no WASM runtime. Published on Packagist as wbreeze/sol-pay-clientcomposer require wbreeze/sol-pay-client. See wasm-client/SPEC.md for the design this mirrors, and wasm-client/README.md for the same crate in Rust/JS form.

What is here, and what is deliberately not

wasm-client/SPEC.md §3 splits an integration into two consumers: a browser, which signs as the payer via a wallet adapter, and a server, which signs as the site authority. This package covers the server row only:

wasm-client/src/core src/Core
pda.rs PdasiteAddress, contractAddress
ix.rs (server-signed subset) IxinitializeSite, meterAndSettle
state.rs Site, Contract, TokenAccount, Mint, Reader (internal)
preflight.rs Preflight, Blocked
error.rs PayError, TokenError, Cause, Shortfall
units.rs Units
program.rs Program
ids.rs Ids
(nothing) Txcompile, wire; see "Transaction assembly" below

The payer-signed instructions — open_contract, renew_contract, close_contract, approve_checked, revoke — and tx.rs's ordered pairing of them are absent on purpose. Wallet Standard is browser JavaScript, so those are signed in the browser regardless of what language the server runs; a PHP port gains nothing by having them. Signing, RPC, and storage are out of scope here for the same reason wasm-client leaves them out — see SPEC §7.

Conventions

Every public pubkey is a base58 string, in and out — matching wasm-client's own JS boundary (wasm-client/README.md, "addresses cross its boundary as base58 strings, never as a Pubkey object"). There is no Pubkey type here at all; Base58::decode/encode are the only place raw address bytes exist, and only internally.

PHP has no unsigned 64-bit integer. This package's safe integer ceiling is PHP_INT_MAX (~9.2e18), not u64::MAX (~1.8e19) — Reader::u64(), Preflight, and Units each document this where it matters. Ordinary token amounts, prices, and limits never come close to either ceiling; the difference only matters at the extreme.

Where Rust splits an API into methods on a deployment handle plus free functions defaulting to the canonical one — avoiding Program::default() at every call site — this package just takes a Program explicitly (Ix, Cause::of) or an optional program id (Pda). Same coverage, one implementation: PHP doesn't have the call-site friction that split exists to avoid in Rust.

Building and testing

Requires PHP ^8.1. Not wired into bin/build-rust or bin/test-rust — this package has no Rust toolchain dependency and no reason to share theirs; see the repository root CLAUDE.md for why that isolation is deliberate rather than an oversight. It has its own entry points instead: bin/test-php and the php conformance workflow, both under "Drift control" below.

That ^8.1 is the library's floor, not the tooling's. PHPUnit 11 pulls a sebastian/* tree requiring php >=8.2, so a plain composer install cannot resolve on 8.1 at all. composer install --no-dev can, because this package has no runtime dependencies — that is how CI tests the floor, and how a consumer on 8.1 would install it, since consumers do not take dev dependencies either.

Drift control

Nothing here can call into the Rust core, so nothing here is provably right without a check against it. vectors-gen/ is that check: a small Rust binary, unpublished, that emits five things sourced from the real crate and program rather than transcribed by hand —

tests/Core/PdaTest.php, IxTest.php, StateTest.php, and ErrorTest.php assert against those values as hardcoded literals, so a mismatch surfaces as a specific failing assertion pointing at the stale one, not a missing file. Regenerate and re-run after touching state.rs, errors.rs, pda.rs, or ix.rs on the Rust side:

Those are two different questions and neither replaces the other. Frozen literals name a local regression precisely and cannot notice the crate moving; freshly generated vectors notice the crate moving and would not tell you which of your own edits broke something.

conformance/vectors.php is what bin/test-php runs, and it checks this package rather than the spike: both PDA families with their bumps, the meter_and_settle data and every account's pubkey and flags, Site::decode, Contract::decode, and both error tables. The php conformance workflow runs it on every push that touches this directory or the program, on PHP 8.1 and 8.5. pda-spike/php/verify.php still exists and still works, but it checks the spike's standalone Pda/Base58 — not what ships.

Preflight and Units are pure arithmetic with no on-chain bytes to cross-check; their tests mirror wasm-client's own #[cfg(test)] modules test-for-test instead. For Units that is the right level of rigor — there is no encoding there to get wrong.

For Preflight it is not, and closing that took a second fixture with different provenance. wasm-client/SPEC.md §8 says why it matters: in pay-on-chain/tests every Rust preflight predicate is checked against actual LiteSVM behaviour, because a predicate that disagrees with the program is worse than having no predicate at all. These copies had no such check. Mirroring the Rust tests test-for-test would not have supplied one — that checks this port against the port's reading of the program, both readings from the same place. Nor could vectors-gen: preflight produces no chain-serialized bytes, which is why it emits none for it, and a verdict computed from the Rust client predicates would only prove PHP agrees with Rust, not with the program.

What supplies it is a recording. pay-on-chain/tests/src/test_preflight_fixture.rs drives the same live SVM the other tests drive and writes conformance/preflight-fixture.json: the three account records as raw bytes at each interesting instant, the predicate's verdict there, and what the program then actually did. conformance/preflight.php replays it. The boundary it crosses is one both sides already agree on — Site::decode, Contract::decode and TokenAccount::decode are themselves checked byte-for-byte on every conformance run — so there is no new schema to keep in step.

Three properties of that arrangement are worth stating, because each one was a choice:

The demonstrator is still the first thing running these predicates against a real devnet, and that remains the sharper test — a recording pins agreement at the states the harness reaches, not at every state a live site produces.

Transaction assembly (SolPay\Tx)

Decided and built 2026-09-04. The reasoning against the obvious shortcut is kept below because it is the part that would otherwise be lost. What was decided is that this package compiles the message itself; see "Decided: this, not the sidecar" for what that was weighed against.

A PHP site authority holding an Instruction from SolPay\Core\Ix cannot send it. Between the instruction and the wire there is a legacy transaction message to compile: compact-u16 (shortvec) length prefixes, account-key deduplication and ordering by signer/writable rank, the three header counts, the program-id index, the recent blockhash, and the signature array. This package stops at the instruction, and nothing in sol-pay goes further in any language — wasm-client/src/core/tx.rs pairs payer-signed instructions in the order the program requires, which is ordering constraints and not wire format. SPEC §7 records the same finding from the specification's side: "the integrator owns the connection" cost nothing for Rust or Node, and the sentence did not change, the population it applies to did.

Why not an existing PHP Solana SDK

There is prior art. Checking it is what settles the question rather than raising it — checked 2026-09-04:

Three consequences, in order of how much they matter:

  1. None of it is checked against anything. These are hand-written ports of a wire format with no conformance vectors and no upstream to notice drift — which is the exact exposure "Drift control" above exists to answer, taken on as a dependency instead. An unverified message serializer fails the way the libsodium PDA shortcut would have: it builds a plausible transaction that does the wrong thing, and then someone signs it.
  2. The dependency cost is disproportionate. This package has zero runtime dependencies and "Building and testing" above makes that load-bearing — composer install --no-dev on 8.1 is how a consumer on the floor installs it. Reaching a message serializer through Guzzle and illuminate/* gives that up for a few hundred lines of encoding.
  3. The floors do not line up. Depending on the maintained fork would push this package's floor from 8.1 to 8.2, and php-conformance.yml tests 8.1 on purpose.

What it would be

Two pure functions of their arguments, which is the property src/Core is built on:

The blockhash is passed in, never fetched. The signature is produced by the caller — sodium_crypto_sign_detached, the signature API, present in ext-sodium since 7.2; only the ed25519 core point API is missing, which is the separate finding in pda-spike/README.md. So every verb in SPEC §7 survives: still no signing, no signature verification, no sign-in message construction, no RPC, no retries, no storage, no routing, no rendering, no session management.

SPEC §2's design rule puts this on the library's side rather than the site's: message compilation is encoding, ordering and exact byte layout; there is no legitimate site choice anywhere in it; and wrong costs a failed transaction and a real fee, which is §2's own criterion. It has no view on what limit to suggest, when to meter, how the site identifies a visitor, or what to do when a payment fails.

Sizing: shortvec, key ordering and header derivation are on the order of 150 lines with no dependencies, against the ~250 for the Fe/Ed25519 field arithmetic already accepted here.

The order this has to happen in

  1. Vectors before the encoder — done. vectors-gen emits the expected message and wire bytes for three fixed (instructions, fee payer, blockhash) cases, produced by solana-message and solana-transaction rather than transcribed by hand. Writing the compiler first would have meant hand-verifying wire bytes, which is the same trap the PDA shortcut was — plausible output, no error. No new surface in the shipped Rust client: the generator reaches for Solana's crates directly.

    The blockhash is sha256("blockhash-0") throughout, following the file's own seeding convention. Signatures are 64 fixed bytes each, seeded per case and position, and are not signatures over anything; they are there so the wire framing around the message is covered for more than one signer. Nothing in sol-pay signs, in any language, and this must not become the place that starts.

    Each vector records the message whole and in pieces — header counts, ordered account keys, compiled instruction indexes — beside the source_instructions that went into it. A byte-for-byte mismatch on the whole says only "differs"; the pieces say whether the ordering, the header or the shortvec framing is what went wrong, which is the same reason the meter_and_settle vector records flags and not just its data.

    The three cases, and what each one is for. One instruction signed and paid for by one key leaves most of compilation unexercised, so the set is chosen to reach every branch:

    • authority-pays — the shipping shape: the site authority signs meter_and_settle and pays. One signature. Nine keys, header 1/0/5.
    • separate-fee-payer — a relayer pays for someone else's instruction. Two signatures, and the authority stays readonly, which is the only way to put anything in the readonly-signer partition. The first case leaves that partition empty, so without this one an encoder could omit it entirely and still pass.
    • two-instructionsinitialize_site and meter_and_settle together, paid by a third key. site is writable in the first and readonly in the second, treasury the reverse, so this pins the flag merge in both directions; both instructions call the same program, so its id must appear once; and two instructions make the instruction array's compact-u16 count something other than 1 for the first time.

    Two rules are worth knowing before writing compile, because both are invisible until a vector disagrees with you:

    • Within each partition the keys ascend by raw pubkey bytes, not by the order the instructions named them. In authority-pays the instruction positions come out 3,5,4 and 7,2,6,–,0 while the bytes ascend strictly. An encoder that preserves instruction order inside a partition builds a different message that still looks right.
    • The fee payer is pulled out and put first rather than sorted into place, and it is forced to a writable signer even when the instruction marked it readonly — it pays the fee. two-instructions is what discriminates this: it has a second writable signer, and sha256("fee-payer-0") sorts after sha256("authority-0"), so an encoder that sorts all the writable signers together passes the other two cases and fails that one.

    While there was no encoder, conformance/vectors.php checked what it could: that each vector was present, internally consistent, and in agreement with the instructions a different crate compiled. Those checks are gone now, replaced by the byte-for-byte comparison they were standing in for — the promise made when they were written, kept rather than left to accumulate.

  2. Then SolPay\Tx — done. compile and wire in src/Core/Tx.php, with tests/Core/TxTest.php asserting hardcoded literals for the same reason the other four suites do: the conformance run notices the crate moving, the literals name which local edit broke something.

    conformance/vectors.php now compares Tx's output to all three vectors byte-for-byte. The shape checks that stood in while there was no encoder are gone, replaced by one diagnostic — a mismatch reports the byte and the section it falls in ("byte 4, in account key 0"), because a hex diff of 348 bytes says only "differs".

  3. Prove it against devnet in the demonstrator — still open. The conformance run proves Tx agrees byte-for-byte with solana-message and solana-transaction on three fixed cases. It does not prove a validator accepts what comes out, and those are different claims: no signature here is real, no blockhash here was ever current, and nothing has paid a fee. With meter_and_settle signed by the site authority, compilation sits on the metering path rather than only in first-run setup, so the demonstrator exercises it on every settling request — the best evidence available, and the first time this code meets a chain.
  4. Then amend SPEC §7's sentence, which §7 already carries as pending. Held until step 3, deliberately: §7 describes what the library does, and "compiles the message that carries them" is a claim better made after a validator has accepted one.

The scope boundary, stated so it is not a surprise later

This package would be shipping a small piece of general Solana plumbing, and people will ask for v0 messages, address lookup tables, compute-budget instructions and durable nonces. The honest boundary is legacy message only, and it holds: a priority-fee or compute-budget instruction is just another Instruction in the array, so it costs nothing to support and nothing to refuse. v0 and lookup tables are a real decision, and for meter_and_settle — eight accounts, one signer — the answer is no.

Decided: this, not the sidecar

Decided 2026-09-04. SPEC §3.1's sidecar was decided the same day, and it absorbs assembly and signing for every unserved language at once — including this one. PHP does not take that route. A sidecar is the answer for a language with no package; PHP has a package, and one whose zero runtime dependencies and 8.1 floor are the whole reason it installs where it has to. Asking a PHP shop for a deployment unit and a trust boundary in place of a composer require gives that up, and the fragmentation that carries the sidecar — Ruby, Java, Scala, ASP.NET, Python, no CMS mass to aim a port at — was never an argument about PHP.

What that buys is bought with drift, and the bill is not disputed here. SPEC §8.1's objection stands exactly as written: this package is a second implementation, SolPay\Tx widens it from derive/encode/decode to derive/encode/decode/compile, and the sidecar would have avoided the widening because Rust gets message compilation from Solana's own crates for nothing. The cost is accepted, not answered. That is what makes step 1 above load-bearing rather than merely prudent: vectors first is the price of this decision, and an encoder written before them would take on the drift without buying the check.

Relationship to pda-spike

pda-spike/php/Curve25519.php and Pda.php were where this package's field arithmetic and PDA derivation were first proven — see pda-spike/README.md for that experiment's own record, including a finding worth knowing before touching Fe/Ed25519: PHP's sodium extension exposes no ed25519 core API on any build tested so far, so the natural-looking shortcut (sodium_crypto_core_ed25519_is_valid_point) doesn't exist, and a stricter substitute would silently derive wrong addresses on roughly half of all inputs.

src/Core/Fe.php and Ed25519.php are promoted copies, namespaced and pruned to what this package actually needs — the spike's own copies, and its libsodium-comparison code, stay frozen as the record of that dated run. Fix a bug in the arithmetic here; don't expect it to also need fixing there, since the spike is a record, not a dependency.

Publishing

Published 2026-09-05, v0.1.0. It stayed packaged-but-unpublished until then on purpose — the same reasoning wasm-client/README.md's publishing section gives: rare, irreversible, and worth a deliberate decision rather than a side effect of finishing the code. What follows is the mechanism, which is unusual enough to be worth reading before the second release.

What the tag actually shipped, confirmed against git archive v0.1.0 in a clone of the mirror: the two licences, this README, composer.json, composer.lock, .gitignore and src/Core. Nothing else — see "What the package ships" above for why, and note that Packagist's recorded dist and source reference for v0.1.0 is 1431093, the mirror's tag, not a commit in the development repository.

Packagist versioning needs more than a tag. Unlike cargo publish/npm publish, which package whatever the manifest says at the moment you run them, Packagist derives a package's version from a git tag — and a tag in this repository is repo-wide, so it would apply to pay-on-chain and wasm-client too, meaning nothing to either of them. Prefixing the tag to scope it (php-client-v0.1.0) looks like the obvious fix and was tested directly against Composer's own VCS driver (the same code Packagist runs): it does not work. Composer's tag parser requires the tag to be the version string, with only an optional leading vv0.1.0 alone resolves to 0.1.0; php-client-v0.1.0 and php-client/v0.1.0 are both invisible to it, not even parsed incorrectly, just never listed as a version at all. A second, independent problem showed up in the same test: Composer's VCS repository type requires composer.json at the repository root, and fails outright (No valid composer.json was found in any branch or tag) when it lives in a subdirectory the way this one does.

Both problems have the same fix, and it is the standard one in the PHP ecosystem (Symfony's components, Laravel's illuminate/*): a subtree split — mirror php-client/ into its own repository and point Packagist at that instead of this one. The split repo gets composer.json at its root for free, and a tag history that cannot collide with the other two artifacts' versioning.

Publishing a version

bin/split-php-client does the local half and stops. It refuses if php-client/ has uncommitted changes, runs bin/test-php and composer test, synthesizes the split, checks the result is actually publishable, and prints the remaining commands. It does not push, tag, or publish: those leave the machine, need personal credentials, and are hard to take back — the same reasoning that leaves cargo publish unscripted.

One-time setup. Create a public repository for the split — it holds only this package. Add it as a remote here, over SSH:

The https:// URL GitHub offers on the repository page is the wrong one to paste. Nothing else here uses it, so it has no credential helper behind it and no key to fall back on: git will not try SSH at all, it will prompt for a GitHub username and password, and password authentication for git operations was removed years ago — so the push fails with Invalid username or token while the SSH key that would have worked sits unused. If that has already happened, git remote set-url split [email protected]:<owner>/<mirror>.git fixes it. The git fetch is what gives bin/split-php-client a refs/remotes/split/master to check against; without it the script says so and checks nothing.

Then submit that repository's URL at https://packagist.org/packages/submit, and enable the GitHub hook Packagist offers so later tags are picked up without resubmitting. Nothing is ever committed in the split repo by hand; see below.

The first push is the one exception, and it needs --force. A repository created through GitHub's own form is not empty — the "Initial commit" it makes for you, and anything added to it since, are commits that exist nowhere in this repository, so the synthesized history shares no ancestor with them and the push is refused as a non-fast-forward. Overwriting them is right rather than regrettable: the split tree is exactly php-client/'s contents, so a hand-written README.md there would be replaced by this file on the first successful push anyway, and a note that belongs to the mirror belongs at the top of this file instead, where every split carries it forward.

The sha has to be the mirror's, and there is a live way to get it wrong. It is what master is on in the mirror — not a commit from this repository, however recently you made it. Read it back rather than typing one you remember: a sha the remote's master has never been at can never satisfy the lease, so the push is refused no matter how many times you run it, and the error talks about stale info rather than about the sha being from the wrong repository.

--force-with-lease rather than --force so it still refuses if the mirror has moved since you looked — the thing worth catching is somebody having committed there, which is the one failure this whole arrangement is trying to avoid. Once, at setup, and never again: every later push is a fast-forward, and one that is not means the history diverged and needs working out, not forcing.

Per release, from the root of this repository:

Then, in a clone of the split repository — not here:

The tag belongs there and only there. A tag in this repository would claim to version pay-on-chain and wasm-client as well, which is the whole problem the split exists to avoid. composer.json carries no version field on purpose — Composer takes it from the tag, and a hardcoded one conflicts with it — and bin/split-php-client fails if one appears.

Which of those two pushes publishes the version? Neither, exactly, and the distinction is worth holding on to. Packagist's hook fires on GitHub's push event, and GitHub sends that for a tag push as much as a branch push. But the hook carries no instruction about what changed: it queues a re-crawl, and Packagist then re-reads the mirror's whole list of branches and tags through Composer's VCS driver and rebuilds the version list from scratch. Tags become releases, branches become dev- versions.

So the branch push refreshes dev-master and finds no tag; the tag push triggers a second crawl that finds one. The tag is discovered, never announced — which is why the order matters only in the sense that whichever push happens last is the one that makes the release visible, and why forgetting the hook is survivable rather than fatal: Packagist crawls a hookless package weekly on its own, and a hooked one at least monthly if a delivery is missed.

Two consequences. A release's metadata comes from composer.json as it exists at that tag — fixing something on master afterwards changes nothing about a version already cut, and needs a new tag. And the tag has to be pushed to the mirror: tagging here does nothing, since the hook is on the mirror and Composer could not parse a scoped tag name anyway (above).

Why this is not a one-time task. The split repository is a projection of a moving source, so every release re-runs the split to pick up what changed. That is cheap: git subtree split is deterministic, so the same commits synthesize to the same SHAs, the branch extends rather than being rewritten, and the push is an ordinary fast-forward. The script checks that property explicitly instead of trusting it, and refuses rather than suggesting a force-push if it ever fails.

The one way to break it: never commit in the split repository. It is write-only from here. A commit made there diverges from the synthesized history, and the next split stops fast-forwarding.

Why the licences are copied, not linked

LICENSE-MIT and LICENSE-APACHE here are intentional duplicates of the ones at the repository root, for the same reason wasm-client/ carries its own: the licence has to be inside the published artifact, and this directory becomes the root of one.

Copies rather than symlinks, and that is not a style preference. Git stores a symlink as a blob whose content is the target string, and git subtree split moves that blob verbatim — so php-client/LICENSE-MIT -> ../LICENSE-MIT arrives at the split root still pointing at ../LICENSE-MIT, now outside the repository entirely. Measured: it dangles in a fresh clone, and it survives git archive as a symlink (the zip entry carries the symlink bit and 14 bytes of path, not the licence text), so it dangles on Composer's dist path too. On a checkout without symlink support it is worse — a plain file whose entire content is ../LICENSE-MIT, which looks real and is not. bin/split-php-client fails on a symlink there rather than shipping one.

What the package ships

Decided 2026-09-05. The split takes everything under php-client/, which includes pda-spike/ — a dated experiment kept as a record — and vectors-gen/, a Rust crate that cannot run for a Composer consumer at all. Neither breaks anything, and both are small in git, but a composer require should not hand somebody a spike and a Rust generator alongside the library.

The suites go too, which is the conventional exclusion in this ecosystem — tests/ and phpunit.xml, and conformance/ with them. That last one is not a judgement call: conformance/vectors.php reads vectors-gen/vectors.json, which is gitignored and has never been in a package, so the script could not run from a dist zip before this either.

Two remedies were available and they are not the same remedy. Excluding all of it from the split would take it out of the published repository too, and it should be there: the split repo is the package's public source, and how the field arithmetic was settled, how the vectors are made, and how anyone checks the claim are all part of that source. Excluding it from git archive takes it out of the dist zip only. That is php-client/.gitattributes:

git archive is what GitHub serves and what Packagist hands Composer as the dist zip, which is how essentially every composer require of this package will arrive. Cloning the split repository, or composer install --prefer-source, still gets everything. That is the line worth drawing: the record stays with the source, and the install gets the library.

composer.lock deliberately stays. It is committed here for a reproducible dev install, Composer ignores a dependency's lock file outright, and excluding it would buy nothing. composer.json's autoload-dev still names the now absent tests/, which is correct and not an oversight — dev autoload rules of a dependency are never used, and composer validate --strict and composer dump-autoload --no-dev were both run against the dist shape to confirm it. What ships is the licences, the README, composer.json, composer.lock and src/Core.

Two mechanical notes, both measured rather than assumed. export-ignore on a directory excludes the whole subtree, even though git check-attr on a file inside it reports the attribute unspecified — check-attr answers per path and does not inherit, git archive prunes at the directory entry. And this file is itself inside the prefix, so it splits with everything else and lands at the split repository's root, where the leading slashes resolve; each path anchors to the directory holding the file, which is php-client here and the package root there — the same two directories either way.

bin/split-php-client extracts the split with git archive, so what it verifies is the dist zip. Its check is a whitelist of the top level, not a list of things to keep out, and that is deliberate: the exclusion is a few lines in one file that nothing else in the repository would notice if they were deleted, and the failure worth guarding against is the next thing added under php-client/ with no line written for it — which a blocklist cannot see. Adding to the package is then a deliberate edit to the script, the same reasoning that leaves publishing unscripted. It still prints the top level on every run.

Licence

Dual licensed under either of Apache License, Version 2.0, or the MIT license, at your option — see the repository root LICENSE.md for why both.


All versions of sol-pay-client with dependencies

PHP Build Version
Package Version
Requires php Version ^8.1
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 wbreeze/sol-pay-client contains the following files

Loading the files please wait ...