serenity_technologies / cashier-nowpayments
Laravel Cashier-style package for NOWPayments cryptocurrency billing
Package info
github.com/Serenity-Technologies/cashier-nowpayments
pkg:composer/serenity_technologies/cashier-nowpayments
Requires
- php: ^8.2
- ext-bcmath: *
- ext-intl: *
- illuminate/database: ^10.0|^11.0|^12.0|^13.0
- illuminate/http: ^10.0|^11.0|^12.0|^13.0
- illuminate/notifications: ^10.0|^11.0|^12.0|^13.0
- illuminate/queue: ^10.0|^11.0|^12.0|^13.0
- illuminate/support: ^10.0|^11.0|^12.0|^13.0
- moneyphp/money: ^4.0
- nesbot/carbon: ^2.0|^3.0
- ramsey/uuid: ^4.0
- serenity_technologies/nowpayments: ^1.0
Requires (Dev)
- kwadwokyeremeh/laravel-package-ide-helper: ^1.0
- laravel/pint: ^1.0
- orchestra/testbench: ^8.0|^9.0|^10.0
- phpunit/phpunit: ^10.0|^11.0
README
A Laravel Cashier-style package for accepting cryptocurrency payments and managing recurring subscriptions via the NOWPayments API.
Features
- One-Time Payments — Direct crypto payments with a built-in checkout overlay UI
- Hosted Invoices — Generate NOWPayments hosted invoice pages with success/cancel redirects
- Recurring Subscriptions — Create plans, subscribe users, swap plans with automatic proration credits
- Crypto Payouts — Send single or batch payouts to external wallet addresses
- Credit System — Plan swap credits tracked with FIFO consumption and pessimistic locking
- Guest Checkout — Unauthenticated users can pay without an account
- Webhook Handling — HMAC + timestamp verified IPN callbacks for payments, invoices, subscriptions, and payouts
- Idempotency — Duplicate payment protection via SHA-256 keyed cache
- JS Modal —
CashierCheckout.open()modal iframe for SPA integrations - Event-Driven — 17 dispatchable events across 5 domains
- Configurable Auth — Payment status endpoints gated behind configurable auth guards
Requirements
| Dependency | Version |
|---|---|
| PHP | 8.2+ |
| Laravel | 10.x, 11.x, 12.x, or 13.x |
| PHP Extensions | ext-bcmath, ext-intl |
| NOWPayments Account | Merchant account with API key |
Installation
composer require serenity_technologies/cashier-nowpayments
Publish Assets
# Config php artisan vendor:publish --tag=cashier-nowpayments-config # Views (checkout overlay) php artisan vendor:publish --tag=cashier-nowpayments-views # JavaScript assets php artisan vendor:publish --tag=cashier-nowpayments-assets
Run Migrations
# Recommended: uses the install command which applies table prefixes php artisan cashier-nowpayments:install # Then run the migrations php artisan migrate
This creates 9 tables: customers, payments, invoices, subscriptions, subscription_items, payouts, payout_withdrawals, credits, and plans.
Environment Variables
Add to your .env:
# API Credentials NOWPAYMENTS_API_KEY=your_api_key NOWPAYMENTS_IPN_SECRET=your_ipn_secret # Defaults CASHIER_NOWPAYMENTS_CURRENCY=usd # Webhook CASHIER_NOWPAYMENTS_WEBHOOK_PATH=/nowpayments/webhook CASHIER_NOWPAYMENTS_WEBHOOK_TOLERANCE=300 # Routes CASHIER_NOWPAYMENTS_ROUTE_PREFIX=cashier-nowpayments CASHIER_NOWPAYMENTS_ROUTE_MIDDLEWARE=web # Payment Behavior CASHIER_NOWPAYMENTS_PAYMENT_METHOD=payment CASHIER_NOWPAYMENTS_FIXED_RATE=false CASHIER_NOWPAYMENTS_FEE_PAID_BY_USER=false # Payment Status Auth CASHIER_NOWPAYMENTS_PAYMENT_STATUS_AUTH=true CASHIER_NOWPAYMENTS_PAYMENT_STATUS_GUARD=web # Caching CASHIER_NOWPAYMENTS_STATUS_CACHE_SECONDS=10 CASHIER_NOWPAYMENTS_PAYMENT_TIMEOUT=900 CASHIER_NOWPAYMENTS_SYNC_COOLDOWN=15 # Notifications CASHIER_NOWPAYMENTS_NOTIFY_PAYMENT_RECEIVED=true CASHIER_NOWPAYMENTS_NOTIFY_PAYMENT_FAILED=true CASHIER_NOWPAYMENTS_NOTIFY_SUBSCRIPTION_ACTIVATED=true
Quick Start
1. Prepare Your Billable Model
Add the Billable trait to your User model (or any billable entity):
<?php namespace App\Models; use Illuminate\Foundation\Auth\User as Authenticatable; use SerenityTechnologies\CashierNowPayments\Billable; class User extends Authenticatable { use Billable; }
2. Create a One-Time Payment
// Fluent builder — creates on NOWPayments + persists locally $payment = $user->newPayment(49.99, 'usd') ->withPayCurrency('btc') ->withDescription('Premium ebook') ->withOrderId('ORDER-123') ->charge(); echo $payment->pay_address; // BTC deposit address echo $payment->pay_amount; // Amount in BTC
3. Create a Hosted Invoice
$invoice = $user->invoice(49.99, 'usd') ->withDescription('Monthly membership') ->withSuccessUrl('https://yoursite.com/success') ->withCancelUrl('https://yoursite.com/cancel') ->generate(); return redirect($invoice->invoice_url);
4. Create a Subscription Plan & Subscribe
// Create the plan on NOWPayments + persist locally $plan = $user->newPlan('premium-monthly') ->withName('Premium Monthly') ->withAmount(29.99) ->withCurrency('usd') ->withIntervalDays(30) ->create(); // Subscribe the user $subscription = $user->newSubscription('default', $plan->id) ->withTrialDays(7) ->create();
5. Send a Payout
// Single withdrawal $payout = $user->payout() ->to('0xAbC...', 'eth', 1.5) ->withDescription('Affiliate commission') ->send(); // Batch payout $payout = $user->payout() ->to('0xAbC...', 'eth', 1.0) ->to('0xDeF...', 'eth', 0.5) ->scheduledFor(now()->addHours(24)) ->send();
6. Use the Checkout Overlay
Blade helper:
{!! $user->checkoutButton(49.99, 'usd', [ 'text' => 'Pay with Crypto', 'description' => 'Order #12345', 'success_url' => route('payment.success'), 'cancel_url' => route('cart'), ]) !!}
JS modal (SPA):
import { CashierCheckout } from './cashier-checkout'; CashierCheckout.open({ amount: 49.99, currency: 'usd', description: 'Premium Plan', success_url: 'https://yoursite.com/success', cancel_url: 'https://yoursite.com/cancel', }).then(result => { console.log('Payment:', result.purchase_id); }).catch(err => { console.log('Cancelled'); });
7. Configure Webhooks
Register your webhook URL in the NOWPayments Dashboard:
https://your-app.com/nowpayments/webhook
The package handles HMAC SHA-512 signature verification and timestamp validation automatically.
Architecture
The Billable Trait
The single Billable trait aggregates 11 concern traits that form the complete API surface:
| Concern | Key Methods |
|---|---|
ManagesCustomer |
customer(), createOrGetCustomer() |
ManagesPayments |
charge(), payments(), remotePayments(), estimateCrypto() |
ManagesInvoices |
invoice(), invoices(), payInvoice() |
ManagesSubscriptions |
newSubscription(), newPlan(), subscription(), subscribed() |
ManagesPayouts |
payout(), payouts(), remotePayouts(), validatePayoutAddress() |
ManagesBalance |
balance() |
ManagesCurrencies |
availableCurrencies(), fullCurrencies(), merchantCoins() |
ManagesConversions |
convert(), remoteConversions() |
ManagesFiatPayouts |
fiatProviders(), supportedFiatCurrencies(), fiatPaymentMethods() |
ManagesPlans |
listPlans(), updatePlan() |
ProvidesCheckoutHelpers |
checkoutButton(), checkoutUrl() |
Database Schema
Billable (User)
└── Customer (MorphOne)
├── Payment[] (HasMany)
├── Invoice[] (HasMany)
├── Subscription[] (HasMany)
│ ├── SubscriptionItem[] (HasMany)
│ └── Credit[] (HasMany)
├── Payout[] (HasMany)
│ └── PayoutWithdrawal[] (HasMany)
├── Credit[] (HasMany)
└── Plan (referenced by Subscription)
Routes
All routes are registered under the configured prefix (default: cashier-nowpayments):
| Method | URI | Middleware | Purpose |
|---|---|---|---|
GET |
/checkout |
web |
Checkout overlay view |
POST |
/checkout/payment |
web, throttle:30,1 |
Create payment (AJAX) |
POST |
/checkout/invoice |
web, throttle:20,1 |
Create invoice + redirect |
POST |
/checkout/subscription |
web, throttle:10,1 |
Create subscription + redirect |
GET |
/checkout/currencies |
web |
List supported currencies (cached 1h) |
POST |
/checkout/estimate |
web, throttle:60,1 |
Get crypto estimate |
GET |
/payment/status/{id} |
web, throttle:30,1, auth |
Poll remote payment status |
GET |
/payment/local/{id} |
web, throttle:30,1, auth |
Check local payment status |
POST |
/nowpayments/webhook |
api |
IPN webhook (no CSRF) |
Documentation
Comprehensive how-to guides are available in docs/how-to/:
| Guide | Covers |
|---|---|
| Installation & Setup | Requirements, install, config, Billable trait, queue, webhooks |
| One-Time Payments | PaymentBuilder, checkout overlay, JS modal, guest checkout |
| Invoice Payments | InvoiceBuilder, hosted invoices, guest invoices |
| Subscriptions & Plans | PlanBuilder, subscribe, swap with proration, cancel |
| Payouts | Batch payouts, scheduled payouts, address validation |
| Credit System | Swap credits, FIFO consumption, expiration |
| Webhooks | IPN config, HMAC verification, webhook handlers, testing |
| Events & Notifications | 17 events, listeners, Laravel notifications |
| Advanced Features | Currencies, conversions, fiat payouts, refunds, custom models |
| Testing & Troubleshooting | Unit/feature testing, common issues, security checklist |
Events
The package dispatches 17 events across 5 domains:
Payment: PaymentCreated, PaymentReceived, PaymentFailed, PaymentStatusSynced, PaymentRefunded
Invoice: InvoiceCreated, InvoicePaid, InvoicePaymentFailed
Subscription: SubscriptionCreated, SubscriptionUpdated, SubscriptionCancelled, SubscriptionExpired, SubscriptionRenewed
Payout: PayoutCreated, PayoutStatusUpdated
Credit: CreditExpired
Listen to events in your EventServiceProvider:
protected $listen = [ \SerenityTechnologies\CashierNowPayments\Events\PaymentReceived::class => [ SendPaymentConfirmationEmail::class, ], ];
Customizing Models
Override any model via config:
// config/cashier-nowpayments.php 'model' => [ 'customer' => \App\Models\Customer::class, 'payment' => \App\Models\Payment::class, // ... ],
Your custom models must extend the package's base models.
Queue Configuration
Events and notifications are dispatched synchronously by default. For production, configure a queue driver:
QUEUE_CONNECTION=database
Then run a queue worker:
php artisan queue:work --tries=3
Security
- Webhook HMAC signatures verified with SHA-512 + constant-time comparison (
hash_equals) - Timestamp tolerance prevents replay attacks (default: 300s)
- Payment status endpoints gated behind configurable auth guards with ownership verification
- Checkout endpoints rate-limited
- Payment creation uses idempotency keys (SHA-256, 5-min cache)
Testing
composer test
Uses Orchestra Testbench. Mock the NOWPayments API facade with NowPayments::shouldReceive().
License
The MIT License (MIT). See LICENSE for details.
Author
Kwadwo Kyeremeh — kyerematics@gmail.com
Built on top of serenity_technologies/nowpayments — the NOWPayments PHP SDK.