kitelab-dev / byl-laravel
Byl.mn төлбөрийн системийн Laravel SDK — нэхэмжлэх, checkout, харилцагч, захиалга, webhook.
Requires
- php: ^8.2
- guzzlehttp/promises: ^2.0
- illuminate/contracts: ^11.0|^12.0|^13.0
- illuminate/http: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
Requires (Dev)
- laravel/pint: ^1.13
- orchestra/testbench: ^9.0|^10.0|^11.0
- pestphp/pest: ^3.0|^4.0
This package is not auto-updated.
Last update: 2026-08-12 07:49:36 UTC
README
Byl.mn төлбөрийн системийн албан ёсны Laravel SDK. Нэхэмжлэх, checkout, харилцагч, захиалга (subscription), billing portal болон webhook-ийг Laravel-д зохицсон байдлаар ашиглана.
use Byl\Laravel\Facades\Byl; $checkout = Byl::checkouts()->builder() ->successUrl(route('purchase.success')) ->addPrice('starter_monthly') ->customer($customer->byl_customer_id) ->create(); return redirect()->away($checkout->url);
- API гарын авлага: https://byl.mn/docs/api/
- Дэмжигдэх хувилбар: PHP 8.2+, Laravel 11 / 12 / 13
Агуулга
- Суулгах
- Тохиргоо
- Нэхэмжлэх
- Checkout
- Харилцагч
- Захиалга (Subscription)
- Billable trait
- Billing portal
- Webhook
- Алдааны боловсруулалт
- Хэд хэдэн төсөл
- Тест бичих
Суулгах
composer require kitelab-dev/byl-laravel
Тохиргооны файлыг (шаардлагатай бол) хуулна:
php artisan vendor:publish --tag=byl-config
Тохиргоо
.env файлд:
BYL_TOKEN=таны-api-token BYL_PROJECT_ID=1 BYL_WEBHOOK_SECRET=таны-webhook-secret
BYL_TOKEN— API токен хуудсанд үүсгэнэ. Токеныг зөвхөн нэг удаа харуулдаг.BYL_PROJECT_ID— төслийн тохиргоо цэснээс харна. Бүх endpoint төсөлд хамаарна.BYL_WEBHOOK_SECRET— webhook-ийн дэлгэрэнгүй хуудсанд байдаг гарын үсгийн түлхүүр (багийн бүх төсөлд ижил).
Нэмэлт тохиргоо (config/byl.php): base_url (жш: https://byl.test), timeout, retry, webhook.route.*.
Test горим. Test горимын төсөлд гүйлгээ 50 төгрөгөөр хязгаарлагдана — код тал нь ижил, өөр токен/төсөл ашиглана.
Нэхэмжлэх
use Byl\Laravel\Facades\Byl; $invoice = Byl::invoices()->create([ 'amount' => 25000, 'description' => 'Гишүүнчлэлийн төлбөр', 'auto_advance' => true, // draft-аас шууд open болгоно ]); $invoice->id; // 5708 $invoice->number; // "DEMO-0011" $invoice->status; // Byl\Laravel\Enums\InvoiceStatus::Open $invoice->url; // төлбөрийн хуудасны хаяг $invoice->dueDate; // Carbon $invoice->isPaid(); // false // Хураангуй хэлбэр $invoice = Byl::invoices()->createFor(25000, 'Гишүүнчлэлийн төлбөр'); Byl::invoices()->find(5708); Byl::invoices()->void(5708); // төлөгдөөгүй нэхэмжлэхийг хүчингүй болгох Byl::invoices()->delete(5708);
Нэхэмжлэх, checkout, portal session объектуудыг controller-оос шууд буцаахад тухайн хуудас руу redirect хийнэ:
public function pay(Order $order) { return Byl::invoices()->createFor($order->total, "Захиалга #{$order->id}"); }
Checkout
Хэрэглэгчийн худалдан авалтыг Byl-ийн hosted checkout хуудсаар зохицуулна — төлбөр, купон, хаяг, баримт зэргийг Byl хийнэ.
$checkout = Byl::checkouts()->builder() ->successUrl(route('purchase.success')) ->cancelUrl(route('cart')) ->addPriceData(15000, 'Гутал', quantity: 2) // шууд үнэ ->addPriceId(3) // Byl дээрх үнийн ID ->addPrice('starter_monthly') // lookup key ->collectPhoneNumber() ->collectDeliveryAddress() ->allowPromotionCodes() // хөнгөлөлтийн кодын талбар ->discount(5400, 'Хямдрал') // нийт дүнгээс хөнгөлөх ->clientReferenceId("order_{$order->id}") ->create(); return redirect()->away($checkout->url);
Массив хэлбэрээр ч дамжуулж болно:
$checkout = Byl::checkouts()->create([ 'success_url' => route('purchase.success'), 'items' => [ ['price' => 'starter_monthly', 'quantity' => 1], ], ]);
Лавлах:
$checkout = Byl::checkouts()->find(13338); $checkout->status; // CheckoutStatus::Open|Complete|Expired $checkout->amountTotal; $checkout->items; // Collection<CheckoutItem> $checkout->couponCodes; // Collection<CouponCode> $checkout->discountTotal();
Хөнгөлөлтийн код. Бүтээгдэхүүний хямдралтай код ашиглах бол бүх item-д
price_id(addPriceId()) хэрэглэнэ. Захиалгын нийт дүнгээс хөнгөлөх кододprice_dataч болно.
Харилцагч
client_reference_id-д өөрийн хэрэглэгчийн ID-г дамжуулбал endpoint нь upsert шиг ажиллана — давхардал үүсгэхгүй тул checkout үүсгэхийн өмнө бүр удаа дуудаж болно.
$customer = Byl::customers()->upsert([ 'email' => $user->email, 'name' => $user->name, 'client_reference_id' => (string) $user->id, ]); $customer->id; // checkout-д дамжуулах customer_id // Лавлах — хариунд эрхтэй захиалгууд хамт ирнэ $customer = Byl::customers()->find(12); $customer = Byl::customers()->findByClientReferenceId((string) $user->id); $customer = Byl::customers()->findByClientReferenceIdOrNull((string) $user->id); // олдоогүй бол null $customer->isSubscribed(); // эрхтэй захиалга байгаа эсэх $customer->subscriptionForLookupKey('starter_monthly'); $customer->subscriptionForProduct(7);
Захиалга (Subscription)
Recurring үнэтэй checkout төлөгдөхөд subscription автоматаар үүснэ (customer_id заавал, ганц item):
Byl::checkouts()->builder() ->customer($customer->id) ->addPrice('starter_monthly') ->successUrl(route('subscribe.success')) ->create();
Сунгалт / багц солилт (одоогийн subscription-ийг дамжуулна):
Byl::checkouts()->builder() ->customer($customer->id) ->subscription($subscriptionId) ->addPrice('growth_monthly') // өөр үнэ → багц солилт, ижил үнэ → сунгалт ->create();
Бусад үйлдлүүд:
$subscription = Byl::subscriptions()->find(4); $subscription->status; // SubscriptionStatus::Trialing|Active|PastDue|Canceled $subscription->isEntitled(); // canceled-аас бусад бүх төлөв → true $subscription->currentPeriodEnd; // эрхийн дуусах хугацаа (Carbon) $subscription->daysUntilPeriodEnd(); $subscription->cancelRequested(); // цуцлалт хүссэн ч мөчлөг дуусаагүй // Жагсаалт (хуудаслалттай). Filter: customer_id, price_id, price, status $page = Byl::subscriptions()->list(['status' => 'active']); $page = Byl::subscriptions()->list(['price' => 'starter_monthly']); $page = Byl::subscriptions()->forCustomer(12, SubscriptionStatus::Active); foreach ($page as $subscription) { /* ... */ } $page->hasMorePages(); // Бүх хуудсыг lazy байдлаар Byl::subscriptions()->all(['status' => 'active'])->each(fn ($subscription) => /* ... */); // Туршилт (төлбөргүй) — 1–365 хоног. Үнийн ID эсвэл lookup key Byl::subscriptions()->startTrial(customerId: 12, price: 3, trialDays: 14); Byl::subscriptions()->startTrial(customerId: 12, price: 'starter_monthly', trialDays: 14); // Цуцлах / буцаах (мөчлөгийн төгсгөлд хэрэгжинэ) Byl::subscriptions()->cancel(4); Byl::subscriptions()->resume(4);
Хэрэглэгчийн эрхийг
subscription.canceledwebhook ирэх хүртэл нээлттэй байлга —trialing,active,past_dueбүгд эрхтэйд тооцогдоно (isEntitled()).
Billable trait
Laravel Cashier шиг модель дээрээ шууд ажиллах хувилбар. Захиалга нь локал byl_subscriptions хүснэгтэд тусах бөгөөд webhook ирэх бүрд автоматаар шинэчлэгддэг тул $user->subscribed() шалгалт сүлжээнд гарахгүй.
php artisan vendor:publish --tag=byl-migrations php artisan migrate
BYL_BILLABLE_MODEL="App\Models\User"
use Byl\Laravel\Billing\Billable; class User extends Authenticatable { use Billable; }
Эрхийн шалгалт (локал)
$user->subscribed(); // эрхтэй эсэх (trialing/active/past_due) $user->subscribed('starter_monthly'); // тодорхой багц дээр $user->subscribedToPrice(3); $user->subscribedToProduct(7); $user->onTrial(); $user->onGracePeriod(); // цуцлалт хүссэн ч мөчлөг дуусаагүй $user->pastDue(); $user->subscriptionEnded(); // эрх нь дууссан $subscription = $user->bylSubscription(); // Eloquent модель $subscription->current_period_end; // Carbon $subscription->daysUntilPeriodEnd(); $user->bylSubscriptions; // бүх захиалга (relation)
Route хамгаалах:
Route::get('/dashboard', ...)->middleware('byl.subscribed'); Route::get('/pro', ...)->middleware('byl.subscribed:growth_monthly');
config('byl.billable.redirect_to') тохируулбал эрхгүй хэрэглэгчийг тэр хаяг руу чиглүүлнэ, эс бөгөөс 403 буцаана.
Захиалга эхлүүлэх
// Recurring checkout — төлөгдмөгц subscription үүсч webhook ирнэ return $user->newSubscription('starter_monthly') ->successUrl(route('subscribe.success')) ->allowPromotionCodes() ->checkout(); // CreatedCheckout (шууд return хийвэл redirect) // Хэд хэдэн мөчлөгийг нэг дор $user->newSubscription('starter_monthly')->cycles(3)->checkout(); // Төлбөргүй туршилт — lookup key эсвэл үнийн ID $user->newSubscription('starter_monthly')->startTrial(14);
Харилцагч Byl дээр байхгүй бол checkout() / startTrial() нь client_reference_id-аар upsert хийж byl_customer_id-г автоматаар хадгална.
Сунгах, багц солих, цуцлах
$subscription = $user->bylSubscription(); return $subscription->renewCheckout(3); // 3 мөчлөгөөр сунгах return $subscription->swapCheckout('growth_monthly'); // багц солих (кредит хасагдана) $subscription->cancel(); // мөчлөгийн төгсгөлд, локал төлөв шууд шинэчлэгдэнэ $subscription->resume(); $subscription->syncFromByl(); // Byl дээрх төлөвөөр дахин таарах
Cashier-аас ялгаатай тал: Монголд автомат суутгал байдаггүй тул
swap(),renew()нь шууд төлбөр авахын оронд checkout буцаадаг (swapCheckout(),renewCheckout()) — хэрэглэгч төлбөрөө өөрөө төлнө.
| Cashier | Byl SDK |
|---|---|
$user->subscribed('default') |
$user->subscribed('starter_monthly') |
$user->subscription() |
$user->bylSubscription() |
$user->newSubscription(...)->checkout() |
$user->newSubscription('starter_monthly')->checkout() |
$user->newSubscription(...)->trialDays(14)->create() |
$user->newSubscription('starter_monthly')->startTrial(14) |
$subscription->swap($price) |
$subscription->swapCheckout($price) |
| — | $subscription->renewCheckout($cycles) |
$user->redirectToBillingPortal() |
$user->redirectToBillingPortal() |
$user->createAsStripeCustomer() |
$user->createOrGetBylCustomer() |
Байгаа өгөгдлөө нөхөх / өөр модель холбох
$user->syncBylSubscriptions(); // Byl дээрх захиалгуудыг локал хүснэгтэд татах $user->asBylCustomer(); // API-аас харилцагчийн мэдээлэл (эрхтэй захиалгын хамт) $user->syncBylCustomerDetails(); // нэр/и-мэйл/утсаа Byl дээр шинэчлэх
client_reference_id нь өгөгдмөлөөр primary key. Өөрөөр холбох бол:
// config/byl.php 'billable' => ['client_reference_column' => 'uuid'], // эсвэл бүрэн өөрийн логик (AppServiceProvider::boot) Byl::resolveBillableUsing(fn (string $reference) => Team::where('slug', $reference)->first());
Модель дээр bylCustomerEmail(), bylCustomerName(), bylCustomerPhone(), bylClientReferenceId() method-уудыг дарж бичиж болно.
Billing portal
Хэрэглэгч захиалгаа өөрөө удирдах self-service хуудас. Session нь 30 минут хүчинтэй тул орох бүрд шинээр үүсгэнэ:
Route::get('/billing', function () { $customer = Byl::customers()->findByClientReferenceId((string) auth()->id()); return Byl::billingPortal()->createSession($customer->id); // portal руу redirect });
Эсвэл URL-ыг өөрөө хэрэглэнэ:
$session = Byl::billingPortal()->createSession($customer->id); $session->url; $session->expiresAt;
Webhook
Package нь POST /byl/webhook endpoint-ийг автоматаар бүртгэж, Byl-Signature гарын үсгийг шалгаад Laravel event болгон илгээнэ. Byl веб хуудсанд webhook нэмэхдээ энэ хаягийг бүртгэнэ.
// app/Providers/AppServiceProvider.php use Byl\Laravel\Events\CheckoutCompleted; use Byl\Laravel\Events\SubscriptionCanceled; use Illuminate\Support\Facades\Event; Event::listen(function (CheckoutCompleted $event) { $checkout = $event->checkout; Order::where('id', $checkout->clientReferenceId)->update(['paid_at' => now()]); }); Event::listen(function (SubscriptionCanceled $event) { User::where('id', $event->subscription->customer?->clientReferenceId) ->update(['plan' => null]); // эрхийг энэ үед хаана });
Event классууд (Byl\Laravel\Events\):
| Event класс | Byl-ийн event |
|---|---|
InvoicePaid |
invoice.paid |
InvoiceVoided |
invoice.void |
CheckoutCompleted |
checkout.completed |
CheckoutExpired |
checkout.expired |
SubscriptionCreated |
subscription.created |
SubscriptionRenewed |
subscription.renewed |
SubscriptionUpdated |
subscription.updated |
SubscriptionRenewalDue |
subscription.renewal_due |
SubscriptionPastDue |
subscription.past_due |
SubscriptionCanceled |
subscription.canceled |
WebhookReceived |
бүх event (танигдаагүй төрөл ч) |
Хаяг эсвэл middleware-г тохируулах:
// config/byl.php 'webhook' => [ 'route' => [ 'enabled' => true, 'path' => 'integrations/byl/webhook', 'middleware' => [], ], ],
Өөрийн controller хэрэглэх бол route-г хааж, byl-signature middleware-ийг залгана:
Route::post('/my/byl-webhook', MyWebhookController::class)->middleware('byl-signature');
Byl амжилтгүй хүсэлтийг 3 хүртэл удаа дахин илгээдэг тул listener-ээ idempotent бичээрэй. Эсвэл BYL_WEBHOOK_PREVENT_DUPLICATES=true болгож ижил event ID-г хаана (sync listener-тэй үед найдвартай).
Алдааны боловсруулалт
Бүх алдаа Byl\Laravel\Exceptions\BylException interface-ийг хэрэгжүүлдэг:
use Byl\Laravel\Exceptions\BylException; use Byl\Laravel\Exceptions\ValidationException; try { Byl::invoices()->create(['amount' => 5]); } catch (ValidationException $exception) { $exception->status(); // 422 $exception->errorFor('amount'); // "The amount must be at least 10." } catch (BylException $exception) { report($exception); }
| Exception | Статус |
|---|---|
AuthenticationException |
401 — токен буруу/хүчингүй |
AuthorizationException |
403 — эрхгүй |
NotFoundException |
404 — олдсонгүй |
ConflictException |
409 — жш: цуцлагдсан захиалгыг дахин цуцлах |
ValidationException |
422 — параметер буруу |
RateLimitException |
429 — retryAfter() |
ServerException |
5xx |
ApiException |
бусад HTTP алдаа |
ConnectionException |
сүлжээ/timeout |
ConfigurationException |
токен/төслийн ID дутуу |
Холболтын алдаа, 429 болон 5xx хариу дээр SDK автоматаар (default 2 удаа) дахин хүсэлт илгээнэ — config('byl.retry').
Хэд хэдэн төсөл
Byl::project(7)->invoices()->createFor(1000); // өөр төсөл, ижил токен Byl::project(7, 'other-token')->invoices()->find(1); // өөр төсөл, өөр токен Byl::withToken('temp-token')->customers()->find(12);
Тест бичих
Byl::fake() нь сүлжээнд гарахгүйгээр бодит бүтэцтэй хариу буцааж, хүсэлтүүдийг шалгах боломж өгнө:
use Byl\Laravel\Facades\Byl; it('захиалга үүсгэхэд checkout үүснэ', function () { $byl = Byl::fake(); $this->post('/orders', ['product' => 'starter'])->assertRedirect(); $byl->assertCheckoutCreated(fn (array $payload) => $payload['items'][0]['price'] === 'starter_monthly'); });
Хариуг өөрөө тодорхойлох, алдаа симуляц хийх:
$byl = Byl::fake() ->respond('GET', 'customers/*', ['id' => 12, 'email' => 'a@b.mn']) ->respondWithError('POST', 'invoices', 422, [ 'message' => 'The amount field is required.', 'errors' => ['amount' => ['The amount field is required.']], ]); $byl->assertSent('GET', 'customers/12'); $byl->assertNotSent('POST', 'checkouts'); $byl->assertSentCount(1); $byl->assertSentForProject(7, 'POST', 'invoices'); $byl->recorded('POST', 'invoices'); // Collection
Webhook-ийг гарын үсэгтэйгээр туршина:
use Byl\Laravel\Enums\WebhookEventType; use Byl\Laravel\Events\SubscriptionCanceled; use Byl\Laravel\Testing\FakeWebhook; it('захиалга цуцлагдахад эрх хаагдана', function () { $webhook = FakeWebhook::subscription(WebhookEventType::SubscriptionCanceled, [ 'id' => 4, 'status' => 'canceled', ]); $this->postJson(route('byl.webhook'), $webhook->payload(), $webhook->headers())->assertOk(); expect($user->fresh()->plan)->toBeNull(); });
FakeWebhook::invoicePaid(), FakeWebhook::checkoutCompleted(), FakeWebhook::make($type, $object, $data) мөн бэлэн байна. Payload-ыг InvoiceFactory, CheckoutFactory, CustomerFactory, SubscriptionFactory (Byl\Laravel\Testing\) хэлбэрээр өөрчилнө.
Хөгжүүлэлт
composer install composer test # vendor/bin/pest composer lint # vendor/bin/pint
Лиценз
MIT — LICENSE.md.