Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Yandex Checkout Sdk Php Laravel Package

yandex-money/yandex-checkout-sdk-php

Official PHP SDK for YooMoney (formerly Yandex.Checkout) that simplifies integrating online payments, refunds, and payouts into PHP applications. Includes API client classes, request/response models, and helpers for common payment flows, helping developers accept payments quickly.

View on GitHub
Deep Wiki
Context7

Getting Started

yoomoney/yookassa-sdk-php is framework-agnostic — Laravel gives you nothing out of the box, so the first hour is wiring, not reading docs.

composer require yoomoney/yookassa-sdk-php:^1.6.9

Look at these four things first, in this order:

  1. YooKassa\Client — the whole API surface: createPayment(), getPayment(), capturePayment(), cancelPayment(), createRefund(), createPayout().
  2. YooKassa\Request\Payments\CreatePaymentRequest — the request object you actually build.
  3. YooKassa\Model\PaymentInterface — the response shape and status constants (pending, waiting_for_capture, succeeded, canceled).
  4. YooKassa\Model\Transfer and YooKassa\Model\Source — changed in 1.6.9 (platform_fee_amount support).

Config + binding:

// config/services.php
'yookassa' => [
    'shop_id'    => env('YOOKASSA_SHOP_ID'),
    'secret_key' => env('YOOKASSA_SECRET_KEY'),
    'return_url' => env('YOOKASSA_RETURN_URL'),
],
// AppServiceProvider::register()
$this->app->singleton(Client::class, function ($app) {
    $client = new Client();
    $client->setAuth(config('services.yookassa.shop_id'), config('services.yookassa.secret_key'));
    $client->setLogger($app->make(LoggerInterface::class));

    return $client;
});

First use case — redirect payment with a fiscal receipt:

$amount = (new Amount())->setValue('1500.00')->setCurrency('RUB');

$request = (new CreatePaymentRequest())
    ->setAmount($amount)
    ->setCapture(true)
    ->setDescription('Order #1042')
    ->setConfirmation(new ConfirmationRedirect(config('services.yookassa.return_url')))
    ->setReceipt((new Receipt())
        ->addItem((new ReceiptItem())
            ->setDescription('Widget')
            ->setQuantity(1)
            ->setAmount($amount)));

$payment = $client->createPayment($request, (string) Str::uuid());

return redirect($payment->getConfirmation()->getConfirmationUrl());

Store $payment->getId() and correlate it with your order before redirecting — the webhook may arrive before the user comes back.

Implementation Patterns

Wrap the client, don't leak it. Put a YooKassaGateway / PaymentService between controllers and YooKassa\Client. Map SDK models to your own DTOs in one place; the SDK's AbstractObject types are awkward in Blade/JSON responses and you'll want to swap the transport later.

Idempotency keys are your responsibility. There is no automatic key generation in older 1.x builds. Generate a UUID v4 once per logical operation, persist it on the order row, and reuse the exact same key on retry. Same key + different body = 400. Max 64 chars.

Webhook handling. The SDK ships YooKassa\Model\Notification\NotificationFactory for parsing, but there is no signature verification. The supported model is:

$notification = (new NotificationFactory())->factoryFromArray($request->all());

// Never trust the body — re-fetch the source of truth
$payment = $client->getPayment($notification->getObject()->getId());

Then: verify the request IP against YooKassa's published ranges, return 200 immediately, and dispatch() a job that re-fetches and transitions your order state. Webhooks are at-least-once.

Two-phase capture. setCapture(false) → waiting_for_capture → capturePayment($id, $amount, $idempotenceKey) when you ship. Good for pre-orders and stock checks.

Split payments (the 1.6.9 feature). platform_fee_amount lets you express the marketplace commission explicitly instead of inferring it from amount:

$transfer = (new Transfer())
    ->setAccountId($sellerAccountId)
    ->setAmount((new Amount())->setValue('1350.00')->setCurrency('RUB'))
    ->setPlatformFeeAmount((new Amount())->setValue('150.00')->setCurrency('RUB'));

$request->setTransfers([$transfer]); // payment amount must be 1500.00

The same parameter is now available on Source for payout requests (sources[]), where it represents the fee withheld on the payout side.

State machine, not event sourcing. Model orders.status transitions (created → pending → paid → refunded) and treat webhooks as one of several triggers. Add a scheduled reconciliation job that sweeps payments stuck in pending older than N minutes via getPayment() — webhooks will get dropped.

Gotchas and Tips

  • Money is never a float. Always pass strings with two decimals ('1350.00'), currency 'RUB'. Compute platform_fee_amount in integer kopecks (or bcmath) and format at the boundary, otherwise 1350.00 + 150.00 != 1500.00 after float rounding and the API rejects the request.
  • The sum rule. For a split payment, transfer.amount + transfer.platform_fee_amount must equal the payment amount. If you only set amount, YooKassa computes the fee as the remainder — so specifying platform_fee_amount is mostly a safety/auditing win, but it also lets you assert the split server-side before the call.
  • Contract gating. platform_fee_amount only works if the shop has split payments enabled and the payment method supports it (bank card, SberPay, SBP — not all methods). Otherwise you get a generic 400 invalid_request. Test in the sandbox against a real shop; a locally-passing test proves nothing here.
  • Upgrade safety. 1.6.9 is additive: Transfer::getPlatformFeeAmount() / Source::getPlatformFeeAmount() return AmountInterface|null, and jsonSerialize() omits nulls, so existing payloads are unchanged. But if you pinned 1.6.8 or lower, setPlatformFeeAmount() is an undefined method — bump the constraint to ^1.6.9 explicitly. If you snapshot exact request JSON in tests, the new key will only appear when you set it, so fixtures stay green.
  • Exception taxonomy. Catch YooKassa\Common\Exceptions\ApiException subclasses (BadRequestException, NotFoundException, ForbiddenException, UnauthorizedException, InternalServerErrorException) separately from ResponseProcessingException (network/timeout — safe to retry with the same idempotence key). Don't catch \Exception around the gateway and swallow it.
  • No retry logic in the bundled CurlClient. Wrap calls with retry(3, fn () => ...) and make sure the idempotence key is generated outside the retried closure.
  • Logging PII. $client->setLogger() sends full request/response bodies to your PSR-3 logger. Receipts contain customer contact data; scrub or use a dedicated channel with a short retention.
  • Receipts are mandatory. If the shop has 54-ФЗ fiscalization, a payment without a Receipt (or with item sums that don't match the payment amount) is rejected. platform_fee_amount does not change the receipt total — the buyer still pays the full amount.
  • Extension points: Client::setApiClient() for a custom HTTP transport (useful for Guzzle/Http:: integration and per-request timeouts), LoggerInterface, and NotificationFactory if you subclass notifications.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib