smlv / sdk
SMLV Platform SDK - Easy integration of SMLV payment system into any SaaS. Includes a CDN-ready JS widget (no iframe) with JWT auth.
Requires
- php: >=7.4
- ext-curl: *
- ext-json: *
- firebase/php-jwt: ^6.0 || ^7.0
Requires (Dev)
- phpunit/phpunit: ^9.0
- dev-main
- v2.3.4
- v2.3.3
- v2.3.1
- v2.3.0
- v2.2.9
- v2.2.8
- v2.2.7
- v2.2.6
- v2.2.5
- v2.2.4
- v2.2.3
- v2.2.2
- v2.2.1
- v2.2.0
- v2.1.0
- v2.0.25
- v2.0.24
- v2.0.23
- v2.0.22
- v2.0.21
- v2.0.20
- v2.0.19
- v2.0.18.1
- v2.0.18
- v2.0.17
- v2.0.16
- v2.0.15.1
- v2.0.15
- v2.0.14.1
- v2.0.14
- v2.0.13
- v2.0.12
- v2.0.11
- v2.0.10
- v2.0.9.1
- v2.0.9
- v2.0.8
- v2.0.7
- v2.0.6
- v2.0.5
- v2.0.4
- v2.0.3
- v2.0.2
- v2.0.1
- 2.0.0
This package is auto-updated.
Last update: 2026-04-30 08:44:00 UTC
README
PHP SDK for integrating SMLV billing into your SaaS application.
v2.2 — The widget now renders directly into your page DOM (no iframe). Account creation and management are handled fully inside the widget — no extra server-side code required from SaaS developers.
Features
- Zero account management — widget auto-resolves or creates the user account on first load
- No iframe — widget renders as native DOM elements, fully styleable
- Secure JWT — short-lived signed tokens (900 s), never exposed in URLs
- Full CRUD widget — Deposit / Balance / Transactions / Account management tabs
- Headless API — use
SmlvClientdirectly for server-side automation - Framework agnostic — plain PHP, Laravel, Yii2, Symfony
Requirements
- PHP 7.4+
firebase/php-jwt^6.0 || ^7.0
Installation
composer require smlv/sdk
Quick Start
1. Initialize the client
use Smlv\Sdk\SmlvClient; $smlv = new SmlvClient([ 'api_url' => 'https://api.smlvcoin.com', 'api_key' => 'your-api-key', 'api_secret' => 'your-api-secret', 'widget_secret' => 'your-widget-secret', ]);
Keep credentials in a config file that is excluded from version control (e.g.
main-local.phpin Yii2,services.phpin Laravel). Never commit real keys to git.
3. Embed a widget
That is literally all you need to show a full billing UI to your user:
use Smlv\Sdk\SmlvWidgetGenerator; $widget = new SmlvWidgetGenerator($smlv); // $subscriber->id — the subscriber ID in your system. // One user may have multiple subscribers — pass the subscriber ID, not the user ID! // // email — optional, used only to pre-fill the account creation form on first visit. // Recommended order: 1) main contact email of the subscriber, 2) current user's email. // If email is unknown — pass an empty string. echo $widget->generateDepositWidget( externalSubscriberId: (string) $subscriber->id, email: $subscriber->contactEmail ?? $currentUser->email ?? '', returnUrl: 'https://your-app.com/billing' );
The widget will:
- Load the SMLV JS bundle from CDN
- Resolve the user's SMLV account (or show a creation form on first visit)
- Render the deposit UI directly in your page
No database column, no createAccount() call, no extra routes required.
Widget Types
// Deposit funds echo $widget->generateDepositWidget($subscriber->id, $email, $returnUrl, $options); // Balance overview + sync echo $widget->generateBalanceWidget($subscriber->id, $email, $options); // Mini inline bar — balance + ⊕ deposit button (ideal for navbars) echo $widget->generateMiniWidget($subscriber->id, $email, $options); // Paginated transaction history echo $widget->generateTransactionsWidget($subscriber->id, $email, $options); // Full account management (overview / edit / danger zone) echo $widget->generateManagementWidget($subscriber->id, $email, $options); // Unified widget: "Create account" OR 4-tab dashboard (Balance | Transactions | Overview | Danger Zone) echo $widget->generateAccountWidget($subscriber->id, $email, $options);
| Type | Best for | Account required? |
|---|---|---|
deposit |
Standalone deposit page | Auto-resolves |
balance |
Balance panel / sidebar | Auto-resolves |
mini |
Top navbar inline bar | No (direct fetch) |
transactions |
Transaction history page | Auto-resolves |
management |
Account settings page | Auto-resolves |
account |
Subscriber detail page | No (handles both) |
All methods return a self-contained HTML snippet:
<div id="smlv-widget-xxxxxxxx" data-smlv></div> <script src="https://cdn.smlvcoin.com/v2.2/smlv-widget.js" async></script> <script> window._smlvQueue = window._smlvQueue || []; window._smlvQueue.push({ token: 'eyJ...', selector: '#smlv-widget-xxxxxxxx', type: 'deposit', options: {}, }); </script>
Options
Common options
| Key | Type | Default | Description |
|---|---|---|---|
theme |
string |
'light' |
'light' or 'dark' |
language |
string |
'en' |
UI language code |
Deposit options
| Key | Type | Description |
|---|---|---|
currencies |
string[] |
Allowed currencies to show |
default_currency |
string |
Pre-selected currency |
Balance options
| Key | Type | Description |
|---|---|---|
show_sync |
bool |
Show Sync button (default true) |
JS callbacks (inline <script> pattern)
window._smlvQueue = window._smlvQueue || []; window._smlvQueue.push({ token: '<?= $widget->generateToken($subscriber->id, $email, 'deposit') ?>', selector: '#my-widget', type: 'deposit', options: { theme: 'dark', onReady: function(instance) { console.log('ready'); }, onSuccess: function(data) { location.reload(); }, onError: function(err) { console.error(err); }, onClose: function() { console.log('closed'); }, } });
Headless API (SmlvClient)
Use SmlvClient directly for server-side operations — background sync, webhooks, reporting, etc.
// Account $account = $smlv->createAccount($email, ['first_name' => 'John', 'external_id' => '42']); $account = $smlv->getAccount($accountReference); $smlv->updateAccount($accountReference, ['last_name' => 'Doe']); $smlv->closeAccount($accountReference); $smlv->reactivateAccount($accountReference); // Balance $balance = $smlv->getBalance($accountReference); $smlv->syncBalance($accountReference); // Transactions $smlv->createTransaction($accountReference, ['amount' => 50, 'currency' => 'USD']); $txns = $smlv->getTransactions($accountReference, ['page' => 1, 'per_page' => 20]); // Lookup $account = $smlv->findAccountByEmail($email);
Webhook Handling
use Smlv\Sdk\SmlvWebhookHandler; $handler = new SmlvWebhookHandler($smlv); try { $event = $handler->handle($_POST, $_SERVER['HTTP_X_SMLV_SIGNATURE'] ?? ''); match ($event['type']) { 'balance.updated' => syncUserBalance($event), 'transaction.completed' => logTransaction($event), default => null, }; http_response_code(200); } catch (\Exception $e) { http_response_code(400); echo $e->getMessage(); }
Webhook events
| Event | Payload fields |
|---|---|
account.created |
account_reference, status |
account.closed |
account_reference |
balance.updated |
account_reference, old_balance, new_balance |
transaction.pending |
transaction_id, amount, type |
transaction.completed |
transaction_id, amount, type, balance |
transaction.failed |
transaction_id, error |
Framework Integration
Laravel
// config/services.php 'smlv' => [ 'api_url' => env('SMLV_API_URL', 'https://api.smlvcoin.com'), 'api_key' => env('SMLV_API_KEY'), 'api_secret' => env('SMLV_API_SECRET'), 'widget_secret' => env('SMLV_WIDGET_SECRET'), ], // AppServiceProvider::register() $this->app->singleton(SmlvClient::class, fn() => new SmlvClient( config('services.smlv.api_key'), config('services.smlv.api_secret'), config('services.smlv.api_url'), config('services.smlv.widget_secret'), ));
Yii2
// common/config/main-local.php ← already in .gitignore 'components' => [ 'smlv' => [ 'class' => \Smlv\Sdk\Yii2\SmlvComponent::class, 'apiUrl' => 'https://api.smlvcoin.com', 'apiKey' => 'pk_live_xxxxxxxxxxxx', 'apiSecret' => 'sk_live_xxxxxxxxxxxx', 'widgetSecret' => 'ws_live_xxxxxxxxxxxx', 'widgetUrl' => 'https://cdn.smlvcoin.com', // CDN base URL 'appUrl' => 'https://smlvcoin.com', // used by generateDepositUrl() // 'widgetScriptVersion' => 'v2.2', // override CDN version (optional) ], ],
main-local.phpis generated fromenvironments/and is listed in.gitignoreby default in the Yii2 advanced template — safe for secrets.
Embed a widget in a view:
// Full account management widget (subscriber detail page) echo Yii::$app->smlv->widgetGenerator->generateAccountWidget( (string) $subscriber->id, $subscriber->email, ['prefill' => ['account_type' => 'legal']] );
Drop-in Yii2 widget class (SmlvBalanceWidget):
For even simpler embedding use the bundled Yii2 Widget class — no generator calls needed:
// Minimal navbar widget (default widgetType = 'mini') echo \Smlv\Sdk\Yii2\SmlvBalanceWidget::widget([ 'subscriberId' => (string) $abonent->id, 'email' => $abonent->email, ]); // Full account widget for subscriber page echo \Smlv\Sdk\Yii2\SmlvBalanceWidget::widget([ 'subscriberId' => (string) $abonent->id, 'email' => $abonent->email, 'widgetType' => 'account', 'prefill' => ['account_type' => 'legal'], ]);
| Property | Type | Default | Description |
|---|---|---|---|
subscriberId |
string |
'' |
Subscriber ID in your system |
email |
string |
'' |
Subscriber e-mail |
widgetType |
string |
'mini' |
'mini' | 'balance' | 'account' |
compact |
bool |
false |
Adds smlv-widget-compact CSS class |
theme |
string |
'light' |
'light' or 'dark' |
language |
string |
auto | BCP-47 tag; defaults to Yii::$app->language |
prefill |
array |
[] |
first_name, last_name, account_type |
widgetOptions |
array |
[] |
Extra options forwarded to the generator |
The mini type automatically generates a deposit redirect URL via generateDepositUrl() — no extra config needed.
Generate a deposit redirect URL (server-side):
$depositUrl = Yii::$app->smlv->generateDepositUrl( (string) $subscriber->id, // account reference Yii::$app->request->absoluteUrl // return URL after deposit );
Generating Snippets Separately
// CDN <script> tag only (place in <head> or before </body>) echo $widget->buildScriptTag(); // async echo $widget->buildScriptTag(defer: true); // defer // Inline init only (place after the <div>) echo $widget->generateInitSnippet( externalSubscriberId: $subscriber->id, email: $email, // optional — see email sourcing note above type: 'balance', options: ['theme' => 'dark'], selector: '#my-balance-widget' ); // Signed JWT token only (for manual JS queue push) $token = $widget->generateToken($subscriber->id, $email, 'deposit');
Auto-charge Behavior (Yii2)
SmlvChargeBehavior deducts a deposit automatically every time a model is saved (EVENT_AFTER_INSERT). All data is provided via callables — no interface implementation required in your model.
Option A — Raw behavior (simple cases)
use Smlv\Sdk\Yii2\SmlvChargeBehavior; class Order extends ActiveRecord { public function behaviors(): array { return [ 'smlvCharge' => [ 'class' => SmlvChargeBehavior::class, 'email' => fn() => $this->user->email, 'amount' => fn() => $this->subtotal * 0.02, // e.g. 2% fee 'description' => fn() => 'Order #' . $this->id, 'metadata' => fn() => [ 'order_id' => $this->id, 'plan' => $this->plan_name, ], ], ]; } }
Option B — Wrapper trait (recommended for SaaS, eGram pattern)
For production SaaS apps, the recommended approach is to put all charge logic into a reusable wrapper trait in your own codebase.
This is exactly the pattern used in eGram via common\traits\smlv\SmlvChargeableTrait.
The trait provides:
smlvBehaviorConfig()— returns readySmlvChargeBehaviorconfig to register inbehaviors()getChargeEmail()— resolves subscriber email (main client contact → abonent admin user fallback)getChargeAmount()— converts EUR price from pricelist to SMLV; returnsnullif the subscriber has no SMLV account (opt-in guard: no account = fall back to bank billing)getChargeDescription()/getChargeMetadata()— human label + EUR/rate/SMLV metadata for reporting- abstract
getSmlvActionType()— the only method your model must implement
In your trait:
// common/traits/smlv/SmlvChargeableTrait.php (your SaaS code) namespace common\traits\smlv; use Smlv\Sdk\Yii2\SmlvChargeBehavior; trait SmlvChargeableTrait { protected function smlvBehaviorConfig(): array { return [ 'class' => SmlvChargeBehavior::class, 'email' => fn() => $this->getChargeEmail(), 'amount' => fn() => $this->getChargeAmount(), 'description' => fn() => $this->getChargeDescription(), 'metadata' => fn() => $this->getChargeMetadata(), ]; } abstract protected function getSmlvActionType(): ?string; public function getChargeAmount(): ?float { // Opt-in guard: skip SMLV charge when the subscriber has no SMLV account // (returns null) — traditional bank billing will apply instead. $accountRef = Yii::$app->smlv->billing->resolveAccountByEmail($this->getChargeEmail()); if ($accountRef === null) { return null; } $eurPrice = SmlvPricelist::getPriceFor($this->getSmlvActionType()); $rate = $this->fetchSmlvRate(); return ($eurPrice && $rate > 0) ? round($eurPrice / $rate, 8) : null; } // ... getChargeEmail(), getChargeDescription(), getChargeMetadata(), fetchSmlvRate() }
In each model:
// common/models/bill/Bill.php use common\traits\smlv\SmlvChargeableTrait; class Bill extends BaseDoc { use SmlvChargeableTrait; public function behaviors(): array { return ArrayHelper::merge(parent::behaviors(), [ 'smlvCharge' => $this->smlvBehaviorConfig(), ]); } // The only method you must implement — everything else is handled by the trait protected function getSmlvActionType(): ?string { return $this->doc_type === 'job_request' ? SmlvPricelist::TYPE_ORDER : SmlvPricelist::TYPE_BILL; } }
How the opt-in guard works:
| Subscriber state | resolveAccountByEmail() |
Result |
|---|---|---|
| Has SMLV account | returns account ref | Charged in SMLV tokens |
| No SMLV account | returns null |
Skipped → bank invoice issued |
smlv component absent |
— | Skipped (CLI, test, dev) |
| API error | throws (caught) | Skipped, warning logged |
Errors are logged to Yii::error(..., 'smlv') and never break the original AR save — the charge is best-effort.
Tip: All four
SmlvChargeBehaviorproperties (amount,description,metadata) accept either acallableor a scalar value. Use callables when you need$thiscontext.
Security
- All API requests are signed with HMAC-SHA256 (
X-API-Key,X-Signature,X-Timestamp) - Widget JWTs: HS256, TTL 900 s, one-time
jticlaim enforced by the server - Tokens are injected into an inline
<script>block — never in a URL or query string - Webhook signatures verified before payload processing
- TLS 1.2+ required
Testing
cd packages/smlv-sdk composer install composer test
Support
- Documentation: https://docs.smlvcoin.com
- API Reference: https://api.smlvcoin.com/docs
- Issues: https://github.com/smlv/sdk/issues
- Email: support@smlvcoin.com
License
MIT — see LICENSE