PFlux DOCS
GitHub Integration enquiries Start integrating
REFERENCE/SDKS

SDKs

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.

PHPJAVASCRIPT / TYPESCRIPT
Packagep2flux/sdk-php@p2flux/sdk
Installcomposer require p2flux/sdk-phpnpm install @p2flux/sdk
Current version0.8.00.8.0
RequiresPHP 8.1+Node 18+, or any runtime with fetch
Runtime dependenciesNoneNone
RegistryPackagistnpm
Sourcegithub.com/P2Flux/sdk-phpgithub.com/P2Flux/sdk-js
PHP
composer require p2flux/sdk-php
JavaScript / TypeScript
npm install @p2flux/sdk

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.

LARAVEL
Packagep2flux/laravel
Installcomposer require p2flux/laravel
Current version0.2.0
Built onp2flux/sdk-php, installed for you
RequiresLaravel 12 or 13, PHP 8.2+
RegistryPackagist
Sourcegithub.com/P2Flux/laravel
Laravel
composer require p2flux/laravel

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.

Getting started · Payments · Network fee in USDC · Subscriptions · Testing · Production checklist · Examples

JavaScript / TypeScript

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.

PHP — Packagist · GitHub · Getting started · Payment flow · Network fee in USDC · Examples · Testing · Production checklist

JavaScript / TypeScript — npm · GitHub · Getting started · Payment flow · Network fee in USDC · Examples · Testing · Production checklist

Payment lifecycleCreate, hand off, verify, fulfil once — and why a browser message is only a claim.Server and browserWhat runs where in JavaScript, and how a bearer capability escapes through a public build variable.Laravel and SymfonyThe PHP client through a container: binding, injection, a repeat-safe verify endpoint.Complete exampleA runnable merchant integration in each language, driven against a canned API.
THE SDKS KEEP NO STATE

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.

import { createPaywall } from '@p2flux/sdk/paywall'

const paywall = createPaywall({ apiUrl: 'https://api.p2flux.com', recipient: '0xYourWallet', price: '0.05' })

app.get('/report', paywall.express(), (req, res) => res.json(report))               // Express
export default { fetch: paywall.wrap((request) => new Response('paid content')) }   // Workers, Bun, Hono, Next
$paywall = new P2Flux\Paywall($p2flux, ['recipient' => '0xYourWallet', 'price' => '0.05']);
$result = $paywall->guard($_SERVER['HTTP_PAYMENT_SIGNATURE'] ?? null, $currentUrl, $_SERVER['HTTP_USER_AGENT'] ?? null);

foreach ($result['headers'] as $name => $value) { header("$name: $value"); }
if (!$result['allow']) { http_response_code($result['status']); echo json_encode($result['body']); exit; }
// serve the content - once per payment
// .env: P2FLUX_RECIPIENT=0xYourWallet
Route::get('/report', ReportController::class)->middleware('p2flux.paywall');          // configured price
Route::get('/data', DataController::class)->middleware('p2flux.paywall:0.20');          // this route's price
Route::get('/article/{id}', ArticleController::class)->middleware('p2flux.paywall:0.05,agents');
  • 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.

const result = await paywall.usage(
  { url: request.url, paymentHeader: request.headers.get('payment-signature'), maxPrice: '1' },
  async () => {
    const rows = await runQuery()
    return { amount: (rows.length * 0.001).toFixed(6), value: rows }
  },
)
if (!result.allow) return new Response(JSON.stringify(result.body), { status: result.status, headers: result.headers })
return Response.json(result.value, { headers: result.headers })
$result = $paywall->usage($_SERVER['HTTP_PAYMENT_SIGNATURE'] ?? null, $currentUrl, '1', function () {
    $rows = run_query();
    return ['amount' => number_format(count($rows) * 0.001, 6, '.', ''), 'value' => $rows];
});
foreach ($result['headers'] as $name => $value) { header("$name: $value"); }
if (!$result['allow']) { http_response_code($result['status']); echo json_encode($result['body']); exit; }
echo json_encode($result['value']);

What each client covers

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.

@p2flux/sdk on npmThe official JavaScript and TypeScript client. npm install @p2flux/sdkp2flux/sdk-php on PackagistThe official PHP client. composer require p2flux/sdk-phpp2flux/laravel on PackagistThe official Laravel integration. composer require p2flux/laravelsdk-js on GitHubSource, tests, guides and runnable examples.sdk-php on GitHubSource, tests, guides and runnable examples.contracts on GitHubSolidity, ABIs and EIP-712 definitions.
Something more than a standard integration?
Marketplace flows, platform billing and custom settlement logic.
Discuss an integration