Download the PHP package scanandpay/php without Composer
On this page you can find all versions of the php package scanandpay/php. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download scanandpay/php
More information about scanandpay/php
Files in scanandpay/php
Package php
Short Description Official Scan & Pay PHP SDK — accept PayTo PayID payments via QR.
License Apache-2.0
Homepage https://docs.scanandpay.com.au
Informations about the package php
scanandpay/php
Official PHP SDK for Scan & Pay — generate PayTo PayID QR codes and payment links from any PHP backend (Laravel, Symfony, Magento, plain PHP, …), then react to our signed webhook when a payment is confirmed. No WordPress or framework required.
Your backend mints a session, we run the payment surface, our webhook confirms back to you.
How the pieces fit
You never handle funds, banking credentials, or PayID resolution — the SDK generates the link, the customer pays on our hosted surface, and you receive a verified webhook telling you the order is paid.
Install
Requires PHP 8.1+ with the curl, json, and hash extensions.
Quickstart
Amount format
amount is always float dollars (e.g. 19.90 for $19.90). This matches
the Scan & Pay API directly — no multiplication or division needed.
For display, use $session->amount directly:
Metadata
Attach a free-form key/value bag to any session. We echo it back unchanged
in the webhook payload + getStatus response, so you can correlate the
payment with your own order/customer/cart records.
Limits: max 50 keys, max 500 chars per key + value (validated client-side before any network call). Don't put secrets here — metadata isn't encrypted at rest in any special way.
API versioning
The SDK pins itself to a date-stamped API contract via the Scanpay-Version
header (e.g. 2026-05-07). When we evolve the wire format, your
already-installed SDK keeps running against the contract it was built for.
Upgrade at your own pace.
Idempotency
Every createSession call sends an Idempotency-Key header. The SDK
generates a UUIDv7 by default; pass your own to make retries safe across
process restarts.
Retries
Transient failures (5xx, network) are retried 3× with exponential
backoff and full-half jitter (≈250 → 500 → 1000ms). 4xx responses
surface immediately. POSTs retry only when an Idempotency-Key header
is present — the SDK auto-sends one for createSession so this is
transparent for the standard path.
Tune by injecting a configured HttpClient:
Webhook secret rotation
Rotate the merchant webhook secret in the dashboard; the SDK accepts both the new and old values during the 30-day grace window:
WebhookVerifier::verify() tries current first, falls back to
previous, then throws WebhookSignatureException.
Errors
All thrown errors extend ScanAndPay\Exceptions\ScanAndPayException.
HTTP-status-mapped subclasses extend ApiException:
| Class | HTTP | Surfaces when |
|---|---|---|
ValidationException |
— | Bad input rejected before any HTTP call |
AuthenticationException |
401 | API rejected X-Scanpay-Key |
AuthorizationException |
403 | Key valid, action not permitted |
NotFoundException |
404 | Session/merchant doesn't exist |
IdempotencyConflictException |
409 | Key in flight or reused with different body |
RateLimitException |
429 | Exhausted rate limit (no retry on 4xx) |
ApiException |
other 4xx | 400, 405, 422, … (no retry) |
ServerException |
5xx | After retries exhausted |
NetworkException |
— | Transport failure after retries exhausted |
WebhookSignatureException |
— | Webhook signature/timestamp/replay check failed |
Credentials
Sign in to the merchant dashboard at business.scanandpay.com.au, open Settings → Integrations, and copy:
Merchant IDAPI Base URLAPI SecretWebhook Secret
Store them in environment variables — never commit them to source control.
Production integration checklist
Use the SDK from your backend only. Your frontend can display the returned payment URL or QR widget, but it must never receive the API Secret or Webhook Secret.
- Create your local order first. Store the cart, customer, amount,
currency, and a durable
platformOrderIdin your own database with a pending payment status. - Create one Scan & Pay session for that order. Call
$client->createSession()from your backend using the sameplatformOrderId. Pass an idempotency key based on your order id if the request may be retried by a queue worker, PHP-FPM retry, or browser refresh. - Render the payment step as pending. Show the returned payment session
to the customer with
scanandpay_checkout(), your own QR renderer, or a redirect to the returned pay URL. Do not send the customer to a success page yet. - Expose a small status endpoint. If you use
scanandpay_checkout(), pointpollUrlat your own backend endpoint. That endpoint should fetch status with the SDK and return only the public status fields your UI needs. - Verify the webhook on raw request bytes. Use
$client->webhooks()->verify($signature, $rawBody)before trusting any webhook payload. A parsed or re-serialized body will fail signature verification. - React to our payment confirmation webhook. Treat
$event->isPaid()as the signal to mark your order paid in your own database. The Place Order click and QR render only mean payment has started — money has not moved until the webhook arrives. - Keep test and live credentials separate. Use environment variables or your secret manager, and never commit merchant credentials into source control, frontend bundles, mobile apps, logs, screenshots, or support tickets.
Safe to share publicly: package install commands, SDK method names, request/response fields, webhook verification rules, idempotency guidance, test-mode behaviour, and error handling. Keep your database schema, internal routes, cloud project names, merchant secrets, and admin tooling private.
Integration pitfalls
Things that bite first-time integrators (we've hit each one ourselves):
- Create the order BEFORE minting the session. The session binds
to your
platformOrderIdand the webhook references the same id when the payment is confirmed. Persist the order in your DB (statuspending) first, then call$client->createSession([...]). - Don't finalise the order on Place Order. A common pattern in
checkout code (WooCommerce included) is a hardcoded list — "if
this gateway is stripe / paypal / etc. defer; otherwise finalise
immediately". If that list is missing
scanandpay, the checkout flashes a success screen and the QR widget never renders. Always treat Scan & Pay as a deferred / async-confirmation gateway, just like Stripe Checkout or PayPal — the customer needs to see the QR and complete the scan-and-pay in their banking app before the order can be considered paid. - Webhook is the source of truth. Mark the order paid only when
the verified webhook arrives with
$event->isPaid()(orevent.status === 'confirmed'if you're inspecting the raw payload). The Place Order click on your site does NOT mean money has moved.
Documentation
- API reference: https://docs.scanandpay.com.au/api/payments
- Webhook payload + signing: https://docs.scanandpay.com.au/api/webhooks
- OpenAPI spec: https://docs.scanandpay.com.au/api-spec.yaml
Local development
Versioning
SemVer. Pre-1.0 — expect minor breaking changes between versions until 1.0.
Licence
Apache-2.0.
All versions of php with dependencies
ext-curl Version *
ext-json Version *
ext-hash Version *