Download the PHP package catidegla/pi-spi-qr without Composer

On this page you can find all versions of the php package catidegla/pi-spi-qr. 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 pi-spi-qr

# PI-SPI QR for PHP Build, parse and validate the interoperable QR payloads of the BCEAO instant payment platform. Eight countries, one franc, no dependencies. [![CI](https://github.com/catidegla/pi-spi-qr/actions/workflows/ci.yml/badge.svg)](https://github.com/catidegla/pi-spi-qr/actions/workflows/ci.yml) [![PHP](https://img.shields.io/badge/php-%E2%89%A58.2-777bb4)](composer.json) [![Dependencies](https://img.shields.io/badge/dependencies-0-brightgreen)](composer.json) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

BCEAO launched PI-SPI on 30 September 2025, and publishes official SDKs for it in JavaScript, Python, Flutter and Java, plus reference apps in React, Angular, Flutter and Django.

There is no PHP one. That is the whole reason this exists.

Generate

Four builders for the four cases the specification describes: static(), staticWithAmount(), dynamic() and transfer(). Feed the result to any QR library you already have. This package does not draw the image, because drawing a QR is a solved problem in PHP and the payload profile is not.

Amounts are whole francs. XOF has no minor unit, so 1500 is one thousand five hundred francs. If you are carrying a money object from elsewhere, convert before you get here, and never through a float.

Read

Validate, which is the part that matters

The specification names two checks to run before processing anything: the scheme identifier, which says the QR is meant for this rail at all, and the checksum, which says nobody altered it since it was printed. problems() runs those first and then the rest of the profile.

It returns every problem rather than stopping at the first, because whoever is holding a QR that will not scan wants the whole list, not one round trip per fault.

What gets checked: the scheme identifier, the checksum, the alias length, the payload format indicator, the currency, that the country is one of the eight member states, the merchant channel, the transaction id length, that a dynamic QR carries a transaction id at all, and that the amount is a whole number of francs.

Why a generic EMV library is not enough

The encoding is ordinary EMV and several PHP libraries already do it. What they cannot know is the profile: that the scheme identifier must be int.bceao.pi, that the currency is always 952, that only a 36 character payment address may appear in a QR while a phone number alias is valid everywhere else on the rail, and that the country must be a union member state. Those rules are what turns a QR that scans into a payment that settles.

Laravel

Nothing to register. The service provider is discovered, and the package still has no runtime dependency: the bridge only wakes up inside a Laravel application and the core works exactly the same without one.

Tested against Laravel 12 and 13. There is no illuminate/* constraint to satisfy, so the version you are on is your business rather than this package's, but those two are the ones CI proves on every commit.

Every QR a merchant issues repeats the same four values. Putting them in config once means the call site carries only what actually varies:

Both return the same QrCode object the core builds, so anything the bridge does not cover is one fluent call away. Pass an alias or a country to either method to override the configured one for a single code.

There is no default country. Any one of the eight would be right for some readers of that file and quietly wrong for the other seven, and a wrong country produces a QR that scans, looks correct, and is refused.

Validating a scanned payload

A payload arriving from a client is user input from an untrusted camera. It may be truncated, altered after printing, or from another payment scheme entirely.

Every broken rule becomes its own message, because a payload that fails on the scheme identifier usually fails on the currency too and the operator wants both at once. Pass new PiSpiPayload(requireAmount: true) for a flow that cannot accept a QR whose amount the payer types.

Reading one at the terminal

The failure people actually hit is a QR that scans and is then refused, and diagnosing that means reading the payload rather than looking at the picture.

That is a real Brazilian Pix code, whose own checksum is perfectly sound. Called with no argument the command builds one from your config instead, prints it, and checks it before you print it on anything.

Conformance

The test suite includes BCEAO's own published worked example, payload and expected checksum both. If it ever fails, this package is wrong about the specification rather than the other way round.

The checksum is CRC-16/CCITT-FALSE: ISO/IEC 13239, polynomial 0x1021, initial value 0xFFFF, no final XOR, no reflection. Half a dozen CRC-16 variants differ only in those last details and all of them get called "CRC-16" in casual writing. Picking the wrong one produces a payload that looks right and is refused at the till, so the initial value alone is covered by a test that fails if you change it to the other common one.

Two things the published specification contradicts itself on

Worth knowing before you trust either.

The merchant channel. The guide documents three values, 000 static, 400 dynamic, 731 transfer. BCEAO's own worked example carries 500, which appears nowhere on the portal. So MerchantChannel is not an enum: an enum would reject their test vector. Any three digit value is accepted, and the documented ones are constants.

The alias. It is described as "an alphanumeric identifier of 36 characters", and every official example is a UUID, which contains hyphens and is therefore not alphanumeric. Length is the rule both halves agree on, so length is enforced and the character set is left alone.

Scope

This does QR payloads. It does not talk to the PI-SPI API, which needs credentials issued to a registered participant, and it deliberately does not render images.

Per the specification, a PI-SPI QR is Merchant-Presented-Mode only, carries no personal data, and must not be used for online or remote payments.

The other half: collecting from a phone

A QR covers a customer standing in front of you. It does nothing for one paying from somewhere else, which on these networks means pushing a prompt to their handset and waiting.

catidegla/laravel-mobile-money does that part: MTN MoMo, Wave and Orange Money behind one Laravel interface, with the driver chosen from the payer's number rather than named by the caller. It writes a row before it calls a provider, so a process that dies mid-call leaves a record rather than a payment nobody knows about, and it reconciles the callbacks that never arrive, which in this region is a normal share of them.

The two are independent and neither requires the other. This one has no runtime dependencies and needs no database; that one is Laravel only and does.

Contributing

The specification is public at developer.pispi.bceao.int. If you find a rule this package gets wrong, an issue quoting the page beats a patch, because the fix usually belongs in the validation messages rather than the encoder.

One caveat on that portal: its "Forum communautaire" link points at forum.pispi.bceao.int, which does not resolve. The sandbox contact [email protected] does work.

License

MIT.


All versions of pi-spi-qr with dependencies

PHP Build Version
Package Version
Requires php Version ^8.2
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 catidegla/pi-spi-qr contains the following files

Loading the files please wait ...