Skip to content

Instantly share code, notes, and snippets.

@plencovich
Last active June 14, 2024 10:22
Show Gist options
  • Save plencovich/acde7aac03c23146c2d5a0cff5656bc3 to your computer and use it in GitHub Desktop.
Save plencovich/acde7aac03c23146c2d5a0cff5656bc3 to your computer and use it in GitHub Desktop.
Breve explicación para poder crear una app en Mercadopago para tener un marketplace

Plenco

Mercadopago Marketplace

Mini tutorial para crear una app de marketplace en mercadopago usando PHP y DX-PHP

Es una ayuda memora en referencia a la excelente información que está en Mercadopago Developers

Creación de APP y Conexión de Usuarios.

  1. Crear una aplicación para marketplace donde:

    • en Redirect URI deben poner la url con https://dominio.com/auth/mercadopago ese formato uso en mi caso; esta url es la que los usuarios van a ser redireccionados luego de confirmar la autorización.
    • tildar los scopes read, write, offline access
    • topicos recomendados: items, orders, create orders, payments
    • url de callback, es la url en donde van a recibir las notificaciones de MP, yo uso https://dominio.com/webhook/mercadopago
  2. Crear un boton en el backend o panel de administración de tu vendedor, para que pueda vincular su cuenta de mercadopago al marketplace. Ese boton debe tener el siguiente formato: https://auth.mercadopago.com.ar/authorization?client_id={APP_ID}&response_type=code&platform_id=mp&state={USER_ID_REF}&redirect_uri=https%3A%2F%2Fdominio.com/auth/mercadopago, donde {APP_ID} es el número que corresponde a tu app de marketplace; {USER_ID_REF} es un código identificador para luego saber que usuario estás conectando y redirect_uri es la misma url que setearon al crear la app.

  3. En el código, en la entrada en donde es redireccionado el vendedor, siguiendo el ejemplo dominio.com/auth/mercadopago tenes que tener un código similar a esto:

    public function auth_provider()
    {
        $code = $this->input->get('code');
        $state = $this->input->get('state');

        // Compruebo que la url tenga el ?code= y el state de mercadopago
        if (isset($code) AND isset($docId)) {

            // Configuro para hacer el POST y obtener el token y datos del usuario

            $url = 'https://api.mercadopago.com/oauth/token';
            $post = '&client_secret='.$this->accessToken.'&grant_type=authorization_code&code='.$code.'&redirect_uri=https://dominio.com/auth/mercadopago';

            $mpResp = $this->utility->curlAPIRestPOST($url,$post,$this->accessToken);

            if ($mpResp->status == 200) {

                // Actualizo en mi DB los datos obtenidos
                $info = array(
                    'mp_access_token' => $mpResp->access_token,
                    'mp_public_key' => $mpResp->public_key,
                    'mp_refresh_token' => $mpResp->refresh_token,
                    'mp_user_id' => $mpResp->user_id,
                    'mp_expires_in' => $mpResp->expires_in,
                    'mp_created_at' => date('Y-m-d H:i:s', time()),
                    'mp_scope' => $mpResp->scope,
                    'mp_live_mode' => $mpResp->live_mode,
                    'mp_token_type' => $mpResp->token_type,
                    'mp_status' => 1
                );

                $update = $this->professionals->update_info($info, $docId);

                // Esto no es necesario pero lo hago para obtener el nick en ML del usuario para que pueda identificar la cuenta
                $url_get = 'https://api.mercadolibre.com/users/me';

                $mpResp = $this->utility->curlAPIRestGET($url_get,$this->accessToken);

                $info = array(
                    'doc_mp_nick_name' => $mpResp->nickname,
                    'doc_email_mercadopago' => $mpResp->email
                );

                $update = $this->professionals->update_info($info, $pdocIdid);
            }
        }
        // Redireccionar a otra web o mostrarle una vista.
        ........
    }

Funciones de cURL GET y POST que utilizo en una librería

public function curlAPIRestPOST($url,$post,$accessToken)
    {
        $curl = curl_init();
        curl_setopt_array($curl,[
            CURLOPT_URL => $url,
            CURLOPT_POST => 1,
            CURLOPT_POSTFIELDS => $post,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 5,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_HTTPHEADER => array(
                "Authorization: Bearer ".$accessToken,
              ),
        ]);

        $response = curl_exec($curl);
        $httpcode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        curl_close($curl);
        $contents = json_decode($response);

        if ($httpcode == 200) {
            $contents->status = 200;
            return $contents;
        } else {
            $contents->status = 400;
            return $contents;
        }
    }

    public function curlAPIRestGET($url, $accessToken)
    {
        $curl = curl_init();
        curl_setopt_array($curl,[
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 5,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_HTTPHEADER => array(
                "Authorization: Bearer ".$accessToken,
              ),
        ]);

        $response = curl_exec($curl);
        $httpcode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        curl_close($curl);
        $contents = json_decode($response);

        if ($httpcode == 200) {
            $contents->status = 200;
            return $contents;
        } else {
            $contents->status = 400;
            return $contents;
        }
    }

Creación de preferencia de pago para checkout

// Set del Access Token del vendedor
MercadoPago\SDK::setAccessToken(ACCESS_TOKEN_DEL_VENDEDOR);

// Si son Mercadopago Dev Certificados, tienen ese código, sino omitir esa linea
MercadoPago\SDK::setIntegratorId(getenv('MP_DEV_CODE'));

$preference = new MercadoPago\Preference();

$item = new MercadoPago\Item();
$item->id = 22233;
$item->title = 'Nombre del Producto o texto para que el comprador identifique que está pagando';
$item->currency_id = 'ARS';
$item->description = 'Breve descripción';
$item->picture_url = 'URL de la imagen del producto';
$item->quantity = 1;
$item->unit_price = (float)$price;

$preference->items = array($item);

$payer = new MercadoPago\Payer();
$payer->name = 'Nombre del Comprador';
$payer->surname = 'Apellido del Comprador';
$payer->email = 'Dirección de Email;
$payer->date_created = 'Fecha de registro del usuario en nuestro sistema, en formato: date('Y-m-d\TH:i:s.vP')';
$payer->identification = array(
    "type" => "DNI",
    "number" => 'Número de DNI'
);
$payer->identification_type = 'DNI';
$payer->identification_number = 'Número de DNI';
$payer->phone = array(
    "area_code" => "54",
    "number" => 'Número de Teléfono'
);
$payer->area_code = '54';
$payer->number = 'Número de Teléfono';

$payer->address = array(
    "street_name" => 'Domicilio',
    "zip_code" => 'Código Postal'
    );

$payer->authentication_type = 'Web Nativa'; // Pueden ser Gmail, Facebook, Web Nativa, Otro.
$payer->registration_date = 'Fecha de registro del usuario en nuestro sistema, en formato: date('Y-m-d\TH:i:s.vP')';
$payer->is_first_purchase_online = 'TRUE o FALSE si es la primera vez que compra.;
$payer->last_purchase = "Si is_first_purchase_online = TRUE deberán ingresar la fecha de la última vez que compró en formato: date('Y-m-d\TH:i:s.vP')";

$preference->payer = $payer;

// Opcional por si quieren quitar métodos de pago de la preferencia y setear las cuotas
$preference->payment_methods = array(
    "excluded_payment_types" => array(
        array("id" => "atm"),
        array('id' => 'bank_transfer'),
        array('id' => 'ticket')
    ),
    "installments" => 12,
    "default_installments" => 1
);

// Opcional para setear las url de pago aprobado, fallido o pendiente
$preference->back_urls = array(
    "success" => 'https://dominio.com/mp/ok',
    "failure" => 'https://dominio.com/mp/error',
    "pending" => 'https://dominio.com/mp/pending'
);

// Retorna siempre
$preference->auto_return = "all";

// Para que no tenga estados pendientes de pago
$preference->binary_mode = TRUE;

// Creación de un código external reference para vincular el pago con un pedido en nuestra DB
$preference->external_reference = $codecart;

// Si van a cobrar una comision por venta
$preference->marketplace_fee = (float)$mp_fee_owner;

// Opcional para setear las url del webhook
$preference->notification_url = 'https://dominio.com/webhook/mercadopago';

// Crea la preferencia
$preference->save();

// Redirecciona al webcheckout de mercadopago, pueden usar este método u otros como poner el link en un boton y mostrar en una vista con el detalle de la compra y el boton pagar.

redirect($preference->{gcfg('mp_mode',NULL)},'refresh');

Información adicional en las preferencias de pago para la prevención de fraude y contracargos:

Configuración del Webhooks

  1. Acceder en la siguiente URL https://www.mercadopago.com/mla/account/webhooks y configurar en modo producción la url donde van a recibir las notificaciones de mercadopago, y tildar los eventos que desean captar, mis recomendados son: pagos, aplicaciones conectadas/desconectadas y split de pagos. Siguiente el ejemplo, usar https://dominio.com/webhook/mercadopago y al presionar probar, le debe dar un response ok 200

  2. Luego crear un código en la ruta del dominio seteado: https://dominio.com/webhook/mercadopago mi código es algo así:

public function webhook()
{
    MercadoPago\SDK::setAccessToken(ACCESS_TOKEN_MARKETPLACE);
    MercadoPago\SDK::setIntegratorId(getenv('MP_DEV_CODE'));
    $info = json_decode($this->input->raw_input_stream);

    if (isset($info->type)) {
        switch ($info->type) {
            case 'mp-connect':
                // Desvinculo de mi sistema cuando el usuario desautoriza la app desde su cuenta de Mercadopago.
                if ($info->action == 'application.deauthorized') {

                    $data_update = array(
                        'mp_access_token' => NULL,
                        'mp_public_key' => NULL,
                        'mp_refresh_token' => NULL,
                        'mp_user_id' => NULL,
                        'mp_expires_in' => NULL,
                        'mp_status' => 0
                    );

                    $this->producers->update_mp_connect($data_update, $info->user_id);
                    $this->output->set_status_header(200);
                    return;
                }

                // Pueden tomar otra acción si el $info->action = 'application.authrized'
            break;

            case 'payment':
                // Actualizo la información de pago recibida.
                $or_collection_id = $info->data->id;
                $info = MercadoPago\Payment::find_by_id($or_collection_id);
                $or_number = $info->external_reference;

                $data_update = array(
                    'or_collection_status' => $info->status,
                    'or_collection_status_detail' => $info->status_detail,
                    'or_payment_type' => $info->payment_type_id,
                    'or_payment_method' => $info->payment_method_id,
                    'or_status' => gcfg($info->status,'or_status_collection_status')
                );

                $this->cart->update_ipn_order($data_update,$or_number);

            break;

            default:
                $this->output->set_status_header(200);
                return;
            break;
        }
    }
    $this->output->set_status_header(200);
    return;
}
@jsolanas7
Copy link

Como marketplace es crucial que el dinero quede 'bloqueado' hasta que el comprador confirme que recibio el producto/servicio. Se puede hacer esto con la API de mercado pago? Ya que si se le envia el dinero al vendedor al momento en que el usuario realiza la compra y luego no provee el servicio/envia el producto, no hay forma de devolverle el dinero (como marketplace) al comprador. Alguna idea? @epontoni @plencovich

@plencovich
Copy link
Author

@jsolanas7 si, si haces un refund, se devuevel el dinero saliendo de ambas cuentas.

@jsolanas7
Copy link

jsolanas7 commented Jun 11, 2024

Y que pasa si el vendedor retira el dinero, como se hace el reembolso en ese caso? No sabes si hay alguna forma de no permitir el retiro del dinero? @plencovich

@plencovich
Copy link
Author

@jsolanas7 el refund se hace igual, te quedará saldo negativo y MP cuando ingreses dinero te lo descuenta

@jsolanas7
Copy link

jsolanas7 commented Jun 14, 2024

@plencovich Pero la cuenta no es mia, es del vendedor. Puedo hacer eso con la cuenta del vendedor?
Ya que estamos te hago una pregunta, cree un usuario de prueba para el marketplace (le hice la aplicacion aqui) y otro de prueba que actua como vendedor. Estoy intentando darle acceso con oauth al markeplace desde la cuenta de test del vendedor pero cuando presiono el boton Authorizar me dice que me envio un email con un codigo de 6 digitos al mail del usuario de test (el cual yo no tengo acceso porq es de test). Ya probe con los 6 ultimos digitos del ID del usuario pero nada. ALguna idea?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment