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

Transbank Sdk Laravel Package

transbank/transbank-sdk

Official PHP SDK for integrating Transbank payment services, including Webpay Plus, Oneclick Mall, Patpass, and full transaction flows. Provides REST/SOAP clients, configuration helpers, and ready-to-use classes for Laravel and PHP apps, simplifying payment initiation, confirmation, refunds, and sta...

View on GitHub
Deep Wiki
Context7

Getting Started

composer require transbank/transbank-sdk

The SDK is a thin REST client — no service provider, no config file, no facades. Everything is driven by an Options object that carries the environment + credentials, and every resource (WebpayPlus\Transaction, WebpayPlus\MallTransaction, Oneclick\MallInscription, Oneclick\MallTransaction, Oneclick\MallBinInfo, TransaccionCompleta\Transaction, Patpass\PatpassComercio\Inscription) is instantiated with ::build($options).

Where to look first (in order):

  1. README.md — it contains a working Webpay Plus create → commit snippet; copy it verbatim as a smoke test.
  2. src/Webpay/Options.php — forIntegration($commerceCode, $apiKey) / forProduction(...) and fromArray() if you want to load from config.
  3. src/Webpay/WebpayPlus/Transaction.php — the reference implementation; every other product mirrors its shape (create, commit, refund, capture, status).
  4. src/Webpay/WebpayPlus/Responses/ — the response DTOs; the getters here are your actual integration surface.

First use case — Webpay Plus, the 90% case. Two endpoints: one that creates the transaction and redirects, one that receives Transbank's POST-back and commits.

use Transbank\Webpay\Options;
use Transbank\Webpay\WebpayPlus\Transaction;

$options = Options::forIntegration('597055555532', '579B532A...A36B1C');
$tx = Transaction::build($options);

// POST /checkout
$response = $tx->create(
    buyOrder:   'ORDER-'.$order->id,
    sessionId:  (string) $order->id,
    amount:     $order->total,      // CLP integer, no decimals
    returnUrl:  route('webpay.return') // must be publicly reachable
);

return redirect($response->getUrl().'?token_ws='.$response->getToken());

// POST /webpay/return  (Transbank POSTs here)
$result = $tx->commit(request('token_ws'));
if ($result->getResponseCode() === 0 && $result->getStatus() === 'AUTHORIZED') {
    $order->markPaid($result->getAuthorizationCode());
}

Integration credentials for Webpay Plus are the well-known public test pair (597055555532 / 579B532A7440BB0C9079DED94D31EA1615BACEB56610332264630D42D0A36B1C); Mall, Oneclick and Patpass each have their own test commerce codes documented in the README. Never ship those into a production config default.

Implementation Patterns

Wrap it once, inject everywhere. The static ::build() is convenient but hostile to testing. Bind a singleton in a service provider and inject it:

$this->app->singleton(Transaction::class, fn () => Transaction::build(
    Options::fromArray([
        'commerce_code' => config('transbank.commerce_code'),
        'api_key'       => config('transbank.api_key'),
        'environment'   => config('transbank.environment'), // integration|production
    ])
));

Two-route controller, nothing more. The checkout route persists a Payment row before calling create() and stores the returned token against the order. The return route looks the order up by token_ws (or buy_order), never by anything in the request body. This is the single design decision that makes the rest of the integration safe.

Status is a state machine, not a boolean. Persist (status, response_code, authorization_code, amount, card_last4, payment_type_code, installments) on every commit, even failures — reconciliation and support tickets live or die on those fields.

Mall / multi-seller. Same flow, but create() takes a $details array (one entry per child commerce code with its own commerce_code, buy_order, amount, installments_number) and commit() returns a detail collection you must iterate — each child has its own response_code. Don't assume the parent-level status implies success for every child.

Oneclick for stored cards. MallInscription::build($options)->start($username, $email, $responseUrl) → user is redirected → finish($token) yields getTbkUser() and getUsername(). Store both; they're the long-lived handle. Subsequent charges are MallTransaction::build($options)->authorize($username, $tbkUser, $parentBuyOrder, $details). Treat tbkUser as a secret (it's a bearer-like card reference) and expect it to be invalidated when the card is reissued — handle the response_code for "inscription not found" by falling back to re-inscription.

Inspecting a stored card (new in 5.1.0). Oneclick\MallBinInfo::build($options)->queryBin($tbkUser) returns BIN-level information for a card already inscribed under your commerce code — useful for showing the card brand/type next to a saved card, or for deciding whether to offer installments, without charging anything. It's the only addition in 5.1.0; every pre-existing method is untouched, so it is a safe minor bump. Cache the result against the inscription row (BIN data changes only when the card is reissued) rather than calling it on every page render, and treat a failed lookup as "unknown card", not as an error that blocks checkout — the charge path (MallTransaction::authorize) is what actually matters.

Deferred capture & refunds. WebpayPlus\Transaction::capture($token, $buyOrder, $authorizationCode, $captureAmount) for deferred flows, refund($token, $amount) for post-settlement. Both need the original token/authorization code — another reason to persist the full commit payload. Refunds on Mall are per child commerce code.

Reconciliation job. A scheduled command that queries Transaction::status($token) for anything stuck in PENDING is worth the 30 lines; Transbank's POST-back will occasionally never arrive (user closed the tab on the redirect page, network blip), and the money will still have moved.

Local development. There is no official fake or sandbox simulator object. Either point at integration (real HTTP, real latency) behind a feature flag, or wrap the SDK behind your own PaymentGateway interface and bind a fake in tests. Trying to mock Guzzle underneath the SDK is more painful than mocking your own interface.

Gotchas and Tips

  • The POST-back is a form POST, not a GET. Your return route must be Route::post(...) and excluded from CSRF (->withoutMiddleware(VerifyCsrfToken::class) or the api middleware group). This is the #1 first-day blocker.
  • Two different failure shapes arrive at the return URL. Success: token_ws. Abort/timeout: TBK_TOKEN + TBK_ORDEN_COMPRA + TBK_ID_SESION and no token_ws — you cannot call commit() in that case. Branch on request()->has('token_ws') before anything else.
  • Don't parse, don't trust. commit() re-fetches the authoritative amount from Transbank. Compare $result->getAmount() against your stored order total and flag mismatches instead of silently accepting.
  • commit() is not idempotent and the browser may double-submit. Guard with a DB transaction + row lock on the order, and short-circuit if the order is already paid. Double fulfillment is the expensive bug in this integration.
  • The 20-minute window is real. Transactions expire; a commit() on an expired token returns a non-zero response_code rather than throwing. Distinguish "expired" from "rejected" in your UX.
  • response_code === 0 is the approval signal, status === 'AUTHORIZED' is corroboration. NULLIFIED and REVERSED are valid states that appear later, so don't treat a non-authorized status as terminal in your own model.
  • Amounts are CLP integers. No floats, no cents, no .00. Passing 1990.0 works; passing 19.90 for a 19-peso charge silently charges 19 pesos.
  • Exceptions are transport-level, not business-level. TransbankException / Guzzle errors mean the call didn't complete — you don't know the outcome. Log the token and reconcile via status() rather than retrying blindly. Configure Guzzle timeouts explicitly if you wrap the client; default timeouts are long enough to hold a PHP-FPM worker hostage.
  • Mall child commerce codes must exist and be linked to the parent in Transbank's portal, otherwise create() fails at runtime with an unhelpful message. Test with the documented test codes before assuming a code bug.
  • BIN data is card metadata, not a token — handle it accordingly. MallBinInfo::queryBin() output identifies a cardholder's card; keep it out of logs, out of client-side JS payloads beyond what you render, and inside the same retention policy as card_last4. It is also not a validation oracle: a 200 response does not prove the card is still chargeable, and a failed lookup does not mean the inscription is dead. Only authorize() tells you that.
  • Pin the version. The project sits at ~61 stars with effectively no third-party dependents, and the SDK has shipped breaking namespace/API changes between majors (configureForIntegration statics → Options + ::build(), Transbank\Webpay namespace restructuring). Lock the major in composer.json, read the changelog before bumping, and expect to be your own support — but note that it is maintained by Transbank directly, with releases through 2025, so the risk is API churn rather than abandonment. 5.1.0 is purely additive (MallBinInfo), so ^5.1 is a low-risk constraint if you're already on 5.x.
  • The SDK does no logging. Add a middleware/listener around your two routes that logs the token, buy order, response code and raw response array. When Transbank support asks for the token, you'll have it.
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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings