PFlux DOCS
GitHub Integration enquiries Start integrating
GET STARTED/QUICKSTART

Quickstart

Take a payment that settles straight to your own wallet. One request to create it, a checkout for the buyer, and one call to prove it landed.

NETWORKBase Sepolia · testnet
ASSETUSDC · 6 decimals
AUTHNone in v1
FEE1% one-time

1. What you need

There is no signup step and no API key, because the API has no authentication in v1. A payment is secured by the customer’s signature and by the contract, not by knowing who called.

  • A recipient address you control — an ordinary EVM address. P2Flux only reads it as a destination.
  • A backend that can POST JSON, to create the payment and to verify it afterwards.
  • The API and checkout URLs of the P2Flux environment you are integrating with. Both are hosted by P2Flux; you do not deploy anything of ours.
  • Testnet USDC on Base Sepolia in a buyer wallet, to try it end to end.
NO KEYS

If you are looking for where to paste an API key: there is nowhere. Anything that says otherwise is describing a product P2Flux is not yet.

2. Point at your API

The API and the checkout are hosted by P2Flux — there is nothing to install and no backend of ours to run. What you need are the two URLs for the environment you are integrating against, which are issued to you rather than discovered. There is no public sandbox and no self-service signup today, so the values below are placeholders.

BEFORE YOU START

You cannot follow this guide end to end without those two URLs, and nothing here points at an endpoint you can call today without them. Ask through integration enquiries.

.env
P2FLUX_API_URL=https://api.p2flux.example
P2FLUX_CHECKOUT_URL=https://checkout.p2flux.example

3. Create a payment

Two fields, both required: the address that receives the money and the amount as a decimal string. Amounts are USDC and accept up to six decimal places.

curl -X POST "$P2FLUX_API_URL/v1/payments" \
  -H "content-type: application/json" \
  -d '{
    "recipient": "0x8fA4bE1a0F2d3C4b5A6978Ee0d1C2b3A4F5e6C21",
    "amount": "100.00"
  }'
use P2Flux\P2FluxClient;

$p2flux = new P2FluxClient(['apiUrl' => getenv('P2FLUX_API_URL')]);

$payment = $p2flux->createPayment([
  'recipient' => '0x8fA4…6C21',
  'amount'    => '100.00',
]);

$payment['intent'];      // hand to the checkout URL
$payment['reference'];   // store against your order

The response carries the signed intent plus everything a checkout needs to build the transaction:

200 OK
{
  "intent": "p2f1.k1.eyJ2IjoxLCJjaGFpbiI6ODQ1MzIs…",
  "reference": "0x9c1f…a7d2",
  "amount": "100.000000",
  "expires_at": 1787200000,
  "pay": {
    "chain_id": 84532,
    "splitter": "0x…",
    "token": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
    "recipient": "0x8fa4…6c21",
    "amount_units": "100000000",
    "reference": "0x9c1f…a7d2"
  }
}
REFERENCE

The reference is 32 random bytes minted by P2Flux — you cannot supply your own. Store it against your order; it is how you tie a confirmed transaction back to what was sold. Order ids, customer ids and emails never reach P2Flux.

The intent expires one hour after it is created by default, so create it when the buyer is ready to pay rather than when the cart is built.

4. Present the payment

Open the hosted checkout with the intent in the URL fragment — the part after the #. Browsers do not include a fragment in the HTTP request or in the Referer header, so it does not reach a server or its logs. It is still visible to anything running in the page, so keep it out of client-side analytics and error reporting.

checkout hand-off
const url = `${CHECKOUT}/#/pay/${encodeURIComponent(payment.intent)}`;
const win = window.open(url, 'p2flux', 'width=460,height=680');

// the checkout announces itself, then reports the result
addEventListener('message', (event) => {
  if (event.data?.type === 'p2flux.ready')
    win.postMessage({ type: 'p2flux.hello' }, new URL(CHECKOUT).origin);

  if (event.data?.type === 'p2flux.payment.completed')
    verifyOnServer(event.data.tx_hash);   // never trust this alone
});

The checkout resolves the intent server-side before it shows anything, so a tampered token fails before the buyer is asked to sign. The buyer signs one transaction: it pays your recipient address and the fee wallet together.

5. Verify it landed

The browser message is a hint, not proof. Verify server-side with the intent and the transaction hash — this is the call that decides whether you fulfil.

curl -X POST "$P2FLUX_API_URL/v1/payments/verify" \
  -H "content-type: application/json" \
  -d '{ "intent": "p2f1.k1.…", "tx_hash": "0x5c1a…90e1" }'
$result = $p2flux->verifyPayment($intent, $txHash);

if ($result['valid'] ?? false) {
  // settled and deep enough to act on
}

A successful verification means all of this held: the receipt exists, the transaction succeeded, the settlement id matches this exact intent, the recipient and token are the ones signed for, the amount and the 1% fee add up, the USDC transfers actually happened, and the transaction is three confirmations deep.

ONE RESPONSE SHAPE

This call answers HTTP 200 whether or not the payment is proven — either the success object below, or { "valid": false, "code": … }. Branch on valid, never on the status code.

200 OK
{
  "valid": true,
  "tx_hash": "0x5c1a…90e1",
  "reference": "0x9c1f…a7d2",
  "amount": "100.000000",
  "block_number": "19284412",
  "block_hash": "0x7b2e…41ac"
}

Everything else comes back as valid: false with a code. The one to handle deliberately is PAYMENT_CONFIRMING, which means the payment cannot yet be proved — either it is not deep enough, or no receipt has been seen for that hash at all. It is not a statement that the money moved, and not a statement that it did not.

valid: trueProven: settled, correct, and confirmed at the required depth. This is the only state that should trigger fulfilment.
PAYMENT_CONFIRMINGNot provable yet — not deep enough, or no receipt seen. Wait and re-verify the same hash. Do not fulfil, do not send the buyer back to checkout, and do not create a second payment.
TRANSACTION_REVERTEDThe transaction failed on chain. Nothing moved.
INVALID_REFERENCEThe receipt does not contain this payment. Do not fulfil.
PAYMENT_ALREADY_PROCESSEDThis exact settlement was already made — the contract refuses a replay.

6. Mark the order paid

P2Flux answers one question — did this payment settle — and holds no opinion about your order. Key your own state on the reference you stored in step 3, and make the transition idempotent: a verify can be repeated safely, and it will be.

PERSIST BEFORE YOU REDIRECT

Store the intent and reference against the order before the buyer leaves for the checkout. There are no webhooks and no lookup by reference, so if the browser callback is lost those two values are what let you reconcile at all — see lost callbacks.

Next steps

One-time paymentsThe full lifecycle and every field.Recurring paymentsAuthorization, renewals, cancellation.API referenceEvery endpoint and status code.TroubleshootingWhat each failure means for the money.
Something more than a standard integration?
Marketplace flows, platform billing and custom settlement logic.
Discuss an integration