OpenReceive for Laravel
Accept Bitcoin & Stablecoin Payments With Laravel
Install the Laravel package, generate the config, host class and migration, then mount the checkout in your existing app. Laravel keeps authentication and orders; OpenReceive owns only payment attempts and settlement.
Package
Tested live · Oct 14, 2026 · 0.4.26
Copy agent directions — Paste into Claude Code, Codex or Cursor to add OpenReceive to your Laravel app.
Why OpenReceive
- One Artisan command.
php artisan openreceive:installwritesconfig/openreceive.php, aHostclass for your three hooks and one migration.php artisan migrateadds the payment tables to the database your app already uses. - Your session, your Eloquent models. The routes mount at
/openreceivein thewebmiddleware group, soauthorizesees the signed-in user and Laravel's CSRF protection applies. Your hooks are ordinary Eloquent queries on your own order model. - Checked at boot, not at checkout. In production the package checks the NWC code when the app boots. A missing code, an unreachable wallet or a code that can spend stops the deploy, not a customer's first checkout.
- Stablecoins from .env. Put a swap provider's code in
LSC_URI_PRIMARYand the package builds the provider itself. Customers can then pay in USDT, USDC, ETH or SOL, and you still receive Bitcoin.
Try the checkout your customers will see
In a Laravel app this checkout is one <openreceive-checkout> tag in a Blade view, talking to the routes the package mounts at /openreceive. The repository's Laravel example runs this same button shop. The live demo needs JavaScript: open the OpenReceive home page.
Six steps, real code
Requires PHP ≥ 8.2 (64-bit), Laravel 11, 12 or 13, ext-gmp.
1. Get a receive-only NWC code — get one here. It can issue invoices and watch for payment, and it can never spend from your wallet.
2. Get an LSC code for swaps (optional) — from a swap provider, only if customers should be able to pay in USDT, USDC, ETH or SOL.
3. Set the environment variables — NWC_URI (required) and
LSC_URI_PRIMARY / LSC_URI_BACKUP (optional) in the environment of
your Laravel server process; see
Environment variables.
4. Install
composer require openreceive/laravel
5. Wire OpenReceive — quoted from the Laravel quickstart.
<?php
namespace App\OpenReceive;
use App\Models\Order; // YOUR model — it could be named anything.
use Carbon\Carbon;
use OpenReceive\Host as OpenReceiveHost;
use OpenReceive\PaymentSettlement;
use OpenReceive\Server\AuthorizeContext;
final class Host implements OpenReceiveHost
{
// Your policy, called before every checkout/payment/swap request.
// $context->action — "checkout.prepare", "checkout.create", "payment.check",
// "swap.quote", "swap.create", "swap.read" or "swap.refund"
// $context->request — the Illuminate\Http\Request: read the session, the
// authenticated user, cookies or headers, as in a controller
// $context->resource — ['reference' => …, 'payment_hash' => ?] copied from the
// payer's JSON body. It names an order; it does not prove
// this caller owns it. reference is always a validated
// non-empty string (≤200 chars); payment_hash is null except
// on payment.check / swap.read / swap.refund.
// Return true to allow, false for a 403. Here: only the signed-in customer
// who placed the order may act on it.
public function authorize(AuthorizeContext $context): bool
{
$order = Order::find($context->reference());
return $order !== null && $order->user_id === $context->request->user()?->id;
}
// The price for a reference — here, your order id — from your own data; null
// when there is nothing to pay for (a 404). `value` is a decimal STRING from
// the order row, never a float and never a request param. `description` is
// what the payer is buying, in your own words.
public function amountFor(string $reference): ?array
{
$order = Order::find($reference);
return $order === null ? null : [
'currency' => 'USD',
'value' => $order->total, // a decimal string column
'description' => $order->items()->count().' items',
];
}
// Runs inside the settlement transaction, only for the order's first settled
// attempt. The WHERE clause is the lock: a second fulfillment path of yours
// (admin action, replayed job) updates zero rows and does nothing. Plain
// Eloquent on the default connection, because the engine holds the
// transaction on that connection's PDO — the query joins it.
public function onPaid(PaymentSettlement $settlement): void
{
Order::where('id', $settlement->reference)
->where('state', 'awaiting_payment')
->update(['state' => 'paid', 'paid_at' => Carbon::createFromTimestampUTC($settlement->paidAt)]);
}
}
6. Render the checkout
npm install @openreceive/elements
Full Laravel quickstart · Example app
How it runs
- Tested on
- PHP 8.2 to 8.4, Laravel 12 and 13
- Databases
- PostgreSQL, MySQL 8 and SQLite
- Secrets
- NWC_URI, and LSC_URI_PRIMARY for swaps, in the server's environment or .env file, never in browser code
- Refunds
- Refund a settled Lightning payment from your own wallet: the wallet code is receive-only. A swap deposit that never converted goes back to the payer through the checkout's refund form.
- Mobile apps
- No app SDK. A mobile app opens your checkout page in a browser tab, which the app stores allow for physical goods and real-world services; digital goods need a link-out program.
Practical questions
- Which currencies can customers pay with?
- Bitcoin over Lightning, always. With a swap provider configured, also USDT on Tron, Solana or Ethereum, USDC on Solana or Ethereum, ETH and SOL, each converted into the same Lightning payment. Automated swaps guide
- Where does the Bitcoin go?
- Into the wallet behind your NWC code; OpenReceive never holds funds. When an order's first payment settles, the package calls onPaid in app/OpenReceive/Host.php inside the settlement transaction. Mark the order paid there, and send mail or dispatch a job from afterPaid, which runs after the commit. Laravel quickstart
- What wallet do I need?
- Any wallet that issues receive-only NWC (Nostr Wallet Connect) codes, hosted or self-run. The code must be able to create invoices and read their status, and must not be able to spend. Get a receive-only NWC code
- What does it cost?
- OpenReceive is open source and free. Fees charged by your wallet provider, by a swap provider or by Lightning routing belong to those services and are outside OpenReceive's scope.
- What do I need to deploy?
- 64-bit PHP 8.2 or later with GMP, Laravel 11, 12 or 13, NWC_URI in .env or the server environment, and the generated migration run with php artisan migrate against your existing database. No cron or queue worker: settlement runs on the package's routes, and php artisan openreceive:notifications is an optional worker for faster confirmation. Laravel quickstart
Add OpenReceive to your Laravel app
Copy agent directions · Full quickstart · Example app · API reference · Guides