Two official clients over the same HTTP API — PHP from Packagist, JavaScript and TypeScript from npm — plus a Laravel integration built on the PHP one. None is required; the API is plain HTTPS.
JS@p2flux/sdk · 0.8.0
PHPp2flux/sdk-php · 0.8.0
DEPENDENCIESNone
REGISTRYnpm and Packagist
Both clients are thin wrappers over the HTTP API. They normalize result codes and nothing else — no scheduling, no storage, no retry loops. Anything either can do, a plain HTTPS request can do too.
FULL PARITY, IN BOTH LANGUAGES
Both official SDKs cover the complete public V1 merchant/server API — the same 21 operations: creating and resolving payment intents, verifying settlements (with settlement receipts), recovery of lost payments and lost charges, subscription setup / resolve / finalize / charge / status, cancellation sessions and preparation, allowance revocation and repair, refunds, and paying the network fee in the payment currency. No raw REST calls are needed for a normal integration in either language. The parity is enforced by a checked-in parity test in each repository and in the API’s own suite, so the two clients cannot drift apart silently. The buyer-side wallet experience is the hosted checkout, not an SDK.
Official SDKs
Two official clients, published on the registry each language already uses. Install from there — the GitHub repositories are the source, not the install route.
Both are server-side clients. They create payments, verify settlements and charge subscriptions from your backend; the buyer’s wallet experience is the hosted checkout, and the only thing a browser does is open it and report back what it saw. That report is a claim — your server’s verification is what marks an order paid.
NO ETH REQUIRED, AND NOT FREE
Both clients expose the sponsored path: with gas_payment_mode: 'payment_token' a buyer holding USDC and no ETH signs instead of sending, the P2Flux relayer supplies the Base ETH, and the buyer reimburses that network cost in USDC inside the same transaction. The fee is quoted before anything is signed. Ask capabilities() before offering it — support is a fact about a deployment, not about a token.
Framework integrations
A framework integration is not a third SDK. It wires an official client into a framework’s container and adds nothing else — the methods you call are still the SDK’s own.
Package discovery registers the service provider, so there is nothing to add to bootstrap/providers.php. Inject P2Flux\P2FluxClient into a controller, command or job and the client is configured from config/p2flux.php. It installs no routes, no migrations, no models and no scheduler: your renewal job stays yours, and installing a package never starts billing anyone.
Zero dependencies. charge() never throws on a payment outcome — an unreachable API comes back as NETWORK_ERROR / RETRY_LATER rather than an exception.
renewal job
import { createP2Flux } from'@p2flux/sdk'const p2flux = createP2Flux({
apiUrl: process.env.P2FLUX_API_URL,
timeoutMs: 30_000// default 60s: a charge waits for confirmation
})
const result = await p2flux.charge(ref)
result.ok // CHARGED or ALREADY_CHARGED
result.action // SUCCESS | WAIT | RETRY_LATER | …
result.retryable // safe to repeat the identical call
result.raw // the untouched API body
METHODWHAT IT DOES
createPayment(terms)Signed one-time payment intent, plus the pay block a checkout needs.
resolvePayment(intent)Authoritative display terms, read back from the intent.
verifyPayment(intent, txHash, receipt?)The trust boundary — a typed verdict on valid, with the settlement receipt.
recoverPayment(intent)Find a settled payment whose transaction hash was lost.
createSubscription(terms)Subscription terms and the signed setup token.
resolveSubscription(setupToken, mode?, payer?)Terms plus the exact EIP-712 payload the customer signs. With 'payment_token' and a payer it also prices the approval P2Flux would send for a wallet holding no ETH.
finalizeSubscription(setupToken, payer, sig, sponsorship?)The customer’s signature → the p2s2. charge capability. A sponsorship that fails is reported beside a subscription that exists either way.
charge(ref)Attempt this period’s payment. Safe to retry.
recoverCharge(ref, periodIndex, hint?)The transaction that charged one exact period, when ALREADY_CHARGED left you a paid period with no hash. Not-found and confirming are results, not exceptions.
status(ref)Current state read from the chain.
createCancellationSession(ref)A browser-safe cancel token — the capability never leaves your server.
prepareSubscriptionCancellation(ref)Calldata the customer’s wallet sends to cancel one subscription.
prepareAllowanceRevocation()Calldata for approve(contract, 0) — the customer’s global stop.
createAllowanceRestoreSession(ref)The p2approve1 session for #/approve/: one approval, no new subscription. Cannot charge, revoke or refund.
resolveAllowanceRestore(token, mode?)What that session authorizes, for the page that holds it. With 'payment_token' it also prices the repair and returns the two messages a customer with no ETH signs.
submitAllowanceRestore(...)Carry those signatures onto the chain. allowance_units: "0" removes the allowance instead — which stops collection, and is not a revocation.
sponsorPayment(intent, quote, payer, sig)Settle a one-time payment the buyer funded with a signature. CONFIRMING means in flight — look it up, never send it twice.
capabilities()Which networks, tokens and operations this deployment really supports. Ask before offering a buyer the option.
prepareRefund(original, amountUnits)Lock the terms of a refund from the original settlement.
resolveRefund(refundToken)What a refund token authorizes, for the page that holds it.
verifyRefund(original, amountUnits, refundTxHash)Confirm a refund happened and is settled.
PHP
PHP 8.1+, no framework and no Composer runtime dependencies. The default transport is P2Flux\CurlTransport, loaded only when no transport is given — the client itself contains no curl call, which is what lets a WordPress plugin vendor it. The constructor takes an optional transport callable, so a host application can route requests through its own HTTP stack — wp_remote_post, Guzzle — or stub them in tests.
PHP
use P2Flux\P2FluxClient;
$p2flux = new P2FluxClient([
'apiUrl' => getenv('P2FLUX_API_URL'),
'timeout' => 30,
'transport' => fn($url, $payload, $timeout) => [$status, $body],
]);
$result = $p2flux->charge($subscriptionRef); // never throws on an outcome
Each repository carries its own guides, runnable examples and a complete merchant integration.
Both clients are thin wrappers over the HTTP API — no scheduling, no storage, no retry loops. That includes refunds: neither tracks whether you have already refunded a payment, so an integration built on them has to enforce refund idempotency itself.
Charging AI agents
Each client can put an x402 paywall in front of a route: a request without payment gets 402 with the price, and the payment is settled before your handler runs. You give your wallet and a price. Needs @p2flux/sdk 0.8.0, p2flux/sdk-php 0.8.0 or p2flux/laravel 0.2.0.
One payment serves one response; the same payment presented again is refused.
Pay per request and prepaid balance are both offered; switch prepaid off with prepaid: false.
agentsOnly (Laravel: ,agents) lets browsers and search engines pass free. A request signed as a bot (Web Bot Auth) counts as an agent.
An agent’s request to take back its unused prepaid balance is answered for you.
Usage pricing — when the cost is known only after the work (tokens, rows, seconds). The agent signs for at most a maximum; you charge what the request cost. The work runs only after P2Flux confirmed the payment will settle; if the settlement then fails, the result is not returned.
Same operations, same names, in both languages — a developer switching between them recognizes every call. All 21 public V1 merchant operations, in both clients:
CALLJSPHP
createPaymentYesYes
resolvePaymentYesYes
verifyPaymentYesYes
recoverPaymentYesYes
createSubscriptionYesYes
resolveSubscriptionYesYes
finalizeSubscriptionYesYes
chargeYesYes
recoverChargeYesYes
statusYesYes
createCancellationSessionYesYes
prepareSubscriptionCancellationYesYes
prepareAllowanceRevocationYesYes
createAllowanceRestoreSessionYesYes
resolveAllowanceRestoreYesYes
submitAllowanceRestoreYesYes
sponsorPaymentYesYes
capabilitiesYesYes
prepareRefundYesYes
resolveRefundYesYes
verifyRefundYesYes
/health is an operational liveness endpoint, not a merchant operation; /metrics and /ready are loopback-only. None of the three belongs in an SDK.
Or use REST
No SDK is required for any part of the integration. Every endpoint is a POST with a JSON body and no authentication — see the API reference. Python and other languages have no client yet; call the API directly.
Each SDK repository carries its own documentation under docs/ — installation, environments, both payment flows, every charge outcome, recovery, allowance repair, refunds and security. The PHP repository also has framework guides, a testing guide and a runnable end-to-end example.