Unlike preferences that redirect users to MercadoPago’s checkout, direct payments allow you to process card payments within your own application using tokenized card data.
Payment flow overview
Tokenize card data
Use MercadoPago’s JavaScript SDK (MercadoPago.js) to securely tokenize card information in the browser. This happens client-side and never exposes raw card data to your server.
Send token to your backend
Your frontend sends the card token and payment details to your Laravel backend.
Create payment
Your backend uses PaymentService to process the payment with MercadoPago’s API.
Handle response
Process the payment response and update your application state accordingly.
Creating payments
Using PaymentService
Using demo endpoint
<? php
use Fitodac\LaravelMercadoPago\Services\ PaymentService ;
use Illuminate\Http\ JsonResponse ;
use Illuminate\Http\ Request ;
final class MercadoPagoPaymentController
{
public function store (
Request $request ,
PaymentService $paymentService
) : JsonResponse {
$payload = $request -> validate ([
'transaction_amount' => [ 'required' , 'numeric' , 'min:0.01' ],
'token' => [ 'required' , 'string' ],
'description' => [ 'required' , 'string' ],
'installments' => [ 'required' , 'integer' , 'min:1' ],
'payment_method_id' => [ 'required' , 'string' ],
'payer' => [ 'required' , 'array' ],
'payer.email' => [ 'required' , 'email' ],
]);
$payment = $paymentService -> create ( $payload );
return response () -> json ( $payment , 201 );
}
}
Payment payload structure
Required fields
{
"transaction_amount" : 100.50 ,
"token" : "CARD_TOKEN" ,
"description" : "Payment description" ,
"installments" : 1 ,
"payment_method_id" : "visa" ,
"payer" : {
"email" : "[email protected] "
}
}
Complete payload example
{
"transaction_amount" : 250.00 ,
"token" : "CARD_TOKEN_FROM_JS_SDK" ,
"description" : "Premium subscription - Monthly" ,
"installments" : 3 ,
"payment_method_id" : "visa" ,
"issuer_id" : 310 ,
"payer" : {
"email" : "[email protected] " ,
"identification" : {
"type" : "DNI" ,
"number" : "12345678"
}
},
"external_reference" : "ORDER-12345" ,
"notification_url" : "https://your-app.com/api/mercadopago/webhooks" ,
"metadata" : {
"user_id" : "98765" ,
"order_id" : "12345"
}
}
Field validation rules
Field Rule Description transaction_amountrequired, numeric, min:0.01Payment amount tokenrequired, stringCard token from JS SDK descriptionrequired, stringPayment description installmentsrequired, integer, min:1Number of installments payment_method_idrequired, stringPayment method (visa, master, etc) issuer_idsometimes, integerCard issuer ID payerrequired, arrayPayer information payer.emailrequired, emailPayer’s email payer.identificationsometimes, arrayID document external_referencesometimes, stringYour order reference notification_urlsometimes, urlWebhook URL metadatasometimes, arrayCustom metadata
Validation rules are defined in src/Http/Requests/CreatePaymentRequest.php
Payment methods
Common payment method IDs:
visa - Visa
master - Mastercard
amex - American Express
debcabal - Cabal Débito
debmaster - Maestro
Getting available payment methods
Query available payment methods for your account:
use Fitodac\LaravelMercadoPago\Services\ PaymentMethodService ;
Route :: get ( '/payment-methods' , function ( PaymentMethodService $service ) {
return response () -> json ( $service -> all ());
});
Or use the demo endpoint:
curl http://localhost:8000/api/mercadopago/payment-methods
Retrieving payment status
After creating a payment, you can retrieve its current status:
Using PaymentService
Using demo endpoint
use Fitodac\LaravelMercadoPago\Services\ PaymentService ;
public function show (
string $paymentId ,
PaymentService $paymentService
) : JsonResponse {
$payment = $paymentService -> get ( $paymentId );
return response () -> json ( $payment );
}
Payment response structure
Successful payment creation returns:
{
"ok" : true ,
"data" : {
"id" : 123456789 ,
"status" : "approved" ,
"status_detail" : "accredited" ,
"transaction_amount" : 100.50 ,
"currency_id" : "ARS" ,
"description" : "Payment for order 1001" ,
"payment_method_id" : "visa" ,
"payment_type_id" : "credit_card" ,
"installments" : 1 ,
"date_created" : "2026-03-10T10:30:00.000-03:00" ,
"date_approved" : "2026-03-10T10:30:01.000-03:00" ,
"payer" : {
"email" : "[email protected] " ,
"identification" : { ... }
},
"card" : {
"first_six_digits" : "450995" ,
"last_four_digits" : "3704" ,
"cardholder" : { ... }
},
"external_reference" : "ORDER-12345"
},
"meta" : []
}
Payment statuses
Status Description approvedPayment approved and credited pendingPayment pending (e.g., awaiting processing) in_processPayment being processed rejectedPayment rejected cancelledPayment cancelled refundedPayment refunded charged_backPayment charged back
Status details
The status_detail field provides additional context:
accredited - Payment successfully credited
pending_contingency - Pending review
pending_review_manual - Manual review required
cc_rejected_bad_filled_card_number - Invalid card number
cc_rejected_bad_filled_date - Invalid expiration date
cc_rejected_bad_filled_security_code - Invalid CVV
cc_rejected_insufficient_amount - Insufficient funds
cc_rejected_high_risk - Rejected by fraud prevention
Handling payment responses
Success scenario
$payment = $paymentService -> create ( $payload );
if ( data_get ( $payment , 'status' ) === 'approved' ) {
// Payment approved
Order :: where ( 'id' , $orderId ) -> update ([
'payment_id' => data_get ( $payment , 'id' ),
'status' => 'paid' ,
]);
return response () -> json ([
'success' => true ,
'payment_id' => data_get ( $payment , 'id' ),
]);
}
Rejection scenario
if ( data_get ( $payment , 'status' ) === 'rejected' ) {
$detail = data_get ( $payment , 'status_detail' );
return response () -> json ([
'success' => false ,
'message' => 'Payment rejected' ,
'reason' => $detail ,
], 422 );
}
Pending scenario
if ( in_array ( data_get ( $payment , 'status' ), [ 'pending' , 'in_process' ])) {
return response () -> json ([
'success' => true ,
'pending' => true ,
'message' => 'Payment is being processed' ,
]);
}
Never rely solely on the immediate payment response. Always implement webhook handling for definitive payment status updates.
Common patterns
Linking payments to orders
$order = Order :: findOrFail ( $request -> order_id );
$payment = $paymentService -> create ([
'transaction_amount' => $order -> total ,
'token' => $request -> card_token ,
'description' => "Payment for order { $order -> id }" ,
'installments' => $request -> installments ,
'payment_method_id' => $request -> payment_method_id ,
'payer' => [
'email' => $order -> customer_email ,
],
'external_reference' => "ORDER-{ $order -> id }" ,
'metadata' => [
'order_id' => $order -> id ,
'user_id' => auth () -> id (),
],
]);
if ( data_get ( $payment , 'status' ) === 'approved' ) {
$order -> update ([
'mercadopago_payment_id' => data_get ( $payment , 'id' ),
'status' => 'paid' ,
]);
}
Error handling
try {
$payment = $paymentService -> create ( $payload );
return response () -> json ([
'success' => true ,
'payment' => $payment ,
]);
} catch ( \ Exception $e ) {
\ Log :: error ( 'Payment creation failed' , [
'error' => $e -> getMessage (),
'payload' => $payload ,
]);
return response () -> json ([
'success' => false ,
'message' => 'Payment processing failed' ,
], 500 );
}
Frontend integration notes
This package handles backend payment processing. For frontend card tokenization, you’ll need to integrate MercadoPago.js separately. The package provides MERCADOPAGO_PUBLIC_KEY for frontend use.
Basic frontend flow:
Load MercadoPago.js with your public key
Collect card data in your form
Use mp.createCardToken() to tokenize card
Send token to your Laravel backend
Backend processes payment using PaymentService
Next steps
Handling Webhooks Process payment status notifications
Refunds Learn how to refund payments
Managing Customers Store customer data and cards