turnley.dev

PHP and MySQL code for faucet operators

Tools & Calculators

FP-005

Send a user to FaucetPay merchant checkout

An auto-submitting form POST with your own reference in the custom field — the field that makes the deposit traceable when it comes back.

Merchant checkout is a form POST to FaucetPay, not a redirect you can build as a URL. Your page renders a form with hidden fields and submits it, and the user lands on FaucetPay to choose a coin and pay.

The field that matters most is custom. Whatever you put there comes back with the payment, and it is the only thing linking the money to a row in your database. Put an id you generated, not a username — usernames change and are guessable.

Create the pending deposit row before you render the form. If the user pays and the callback arrives before you have anything to match it to, you are reconciling by hand.

PHP
function merchant_checkout_form(int $userId, float $usd, string $itemName): string
{
    // Create the pending row FIRST. The custom value has to point at something.
    $ref = bin2hex(random_bytes(12));
    q('INSERT INTO deposits (user_id, ref, usd, status, created_at) VALUES (?, ?, ?, "pending", NOW())',
      [$userId, $ref, $usd]);

    $fields = [
        'merchant_username' => setting('fp_merchant_username'),
        'item_description'  => $itemName,
        'amount1'           => number_format($usd, 8, '.', ''),   // USD
        'currency1'         => 'USD',
        'custom'            => $ref,                              // comes back verbatim
        'callback_url'      => url('deposit_callback.php'),
        'success_url'       => url('deposit_done.php'),
        'cancel_url'        => url('deposit_cancel.php'),
    ];

    $html = '<form id="fp" method="post" action="https://faucetpay.io/merchant/webscr">';
    foreach ($fields as $name => $value) {
        $html .= '<input type="hidden" name="' . e($name) . '" value="' . e((string) $value) . '">';
    }
    $html .= '<noscript><button type="submit">Continue to FaucetPay</button></noscript>';
    $html .= '</form><script>document.getElementById("fp").submit();</script>';

    return $html;
}

Using it

Include the noscript button. Without it a visitor with JavaScript disabled sees an empty page and no way forward.

Store the USD amount you asked for on the pending row and compare it against the verified amount later. A callback claiming a different figure is either a bug or an attempt.

What bites people

Do not treat arriving at success_url as payment. That is a browser redirect the user controls; the callback plus a server-side verify is the proof.

A user can abandon checkout, so pending rows accumulate. Expire them after a day rather than leaving them to confuse your reconciliation.

This one touches real money. Point it at a throwaway wallet and watch a full cycle before you trust it with a live balance.

Also in FaucetPay