-----------------------------------------------------------------------------
Bancomer API v1 Client for PHP (version 1.0.0)
-----------------------------------------------------------------------------
El objetivo de este proyecto es proporcionar un framework reutilizable para 
interacturar con el API de Bancomer desde cualquier proyecto PHP.   

-----------------------------------------------------------------------------
Autor: Bancomer SAPI de C.V. (soporte@openpay.mx)
       http://www.openpay.mx/
-----------------------------------------------------------------------------


-- REQUERIMIENTOS --
PHP 5.2 o posterior 
Extension de PHP cURL
Extension de PHP JSON
Extension de PHP Multibyte String


-- INSTALACION --

Para poder utilizar la librería cliente de Bancomer para PHP será necesario:
  1. Descomprimir el archivo Bancomer.v1.zip dentro de un directorio del 
     proyecto PHP en el cual se utilizará,
  
  2. Incluir dentro de la(s) página(s) que hará(n) uso del cliente:
    
     require(dirname(__FILE__) . '/Bancomer.php');
     

NOTA: En el ejemplo anterior, la librería está dentro del mismo directorio 
del script que se está ejecutando, en caso de que éste no sea el caso se 
deberá hacer los ajustes pertinentes en la ruta de la librería.

 
-- IMPLEMENTACION --

Configuración

Antes de utilizar la librería será necesario configurar el ID de comercio 
(Merchant ID) y la llave privada que corresponda (API Key). Hay varias 
opciones:

  1. Utilizar los métodos Bancomer::setId() y Bancomer::setApiKey(). Para 
     utilizar este método solamente hay que pasar el dato correspondiente
     a ambos métodos:
     
	 Bancomer::setId('moiep6umtcnanql3jrxp');
	 Bancomer::setApiKey('sk_3433941e467c4875b178ce26348b0fac');
	
  2. Pasar ambos datos como parámetros al método Bancomer::getInstance(). 
     Este método es muy similar al anterior, solamente que ambos parámetros se
     pasan simultáneamente al generador de la instancia. El orden de los 
     métodos es Merchant ID y API Key, respectivamente 

	 $bancomer = Bancomer::getInstance('moiep6umtcnanql3jrxp', 
	                                 'sk_3433941e467c4875b178ce26348b0fac');
	
  3. Configurar ambos datos como variables de ambiente. Este método permite la 
     configuración de ambos datos pero a nivel de ambiente, lo cual tiene sus 
     ventajas ya que no están expuestos directamente en un archivo (para mayor
     referencia leer la documentación de PHP). Esta opción no requiere de 
     ninguna configuración a nivel script. 


Modo Sandbox

Cuando se está realizando la implementacion es posible que se desee hacer 
pruebas antes de que se hagan cobros normales a una tarjeta de crédito, para 
ello es posible utilizar el método Bancomer::setSandboxMode() el cual nos 
ayudará a activar o desactivar el modo de prueba en las peticiones que se hagan
a OpenPay.

	 Bancomer::setSandboxMode(FLAG);

Si es necesario, se puede utilizar el método Bancomer::getSandboxMode() para 
determinar, en cualquier momento, cual es el estatus del modo sandbox:

	Bancomer::getSandboxMode(); // TRUE/FALSE, dependiendo si el modo está 
	                           // activado o no.


Generalidades librería PHP

Una vez configurado el cliente, para realizar operaciones con la librería será 
necesario obtener una instancia de la misma, esto se hace con el método 
Bancomer::getInstance()

	$bancomer = Bancomer::getInstance();

$bancomer se tratará entonces, de la instancia del merchant desde la que se 
podrán llamar todos los recursos disponibles en la API de Bancomer:

  1. customers
  2. cards
  3. charges
  4. payouts
  5. fees
  6. plans

Todos los recursos se acceden como variables públicas, de tal manera, que si 
se desea agregar un nuevo cliente se podrá hacer de la siguiente manera:

	$bancomer->customers->add(PARAMETROS);
	
Hay que notar que las operaciones de todos y cada uno de los recursos, 
devolverán una instancia de dicho recurso, lo que en el ejemplo anterior querrá
decir que la llamada a la operación add() en el recurso Customer devolverá una
instancia del mismo recurso Customer, y así sucesivamente. La única excepción a 
este caso es cuando se obtiene un listado de recursos, en cuyo caso el dato 
devuelvo será un arreglo de recursos:

	$customer = $bancomer->customers->add(PARAMETROS);
	// devuelve UNA SOLA instancia del recurso Charge
	$charge = $customer->charges->create(PARAMETROS);

	
	$customer = $bancomer->customers->add(PARAMETROS);
	// devuelve UN ARREGLO de instancias del recurso Charge
	$chargeList = $customer->charges->getList(PARAMETROS);

Los recursos derivados de Customer son:

  1. cards
  2. bankaccounts
  3. charges
  4. transfers
  5. payouts
  6. suscriptions

Todos los parámetros deben ser arreglos asociativos de la forma:

	'NOMBRE_PARAMETRO' => VALOR

Para mayor referencias respecto a los parámetros, datos obligatorios y valores 
ceptados, consultar la documentación del API Bancomer.  


Manejo de errores

El API de Bancomer genera distintos tipos de errores de acuerdo al problema que
se haya detectado, por eso, a fin de que el manejo de los errores generados por
la librería sea mejor y más administrable y eficiente, la librería PHP ha 
implementado excepciones para las diferentes situaciones:

  1. BancomerApiTransactionError. Esta categoría engloba todas aquellas 
     situaciones en las que una transacción no se haya completado exitosamente,
     por ejemplo: tarjeta declinada, tarjeta con fondos insuficientes, cuenta
     destino desactivada, etc.
  2. BancomerApiRequestError. Se refiere a los errores generados cuando se hace
     una petición al API. Ejemplos: formateo incorrecto en laos datos de la 
     petición, valores incorrectos en los parámetros de la petición, errores 
     internos en el servidor Bancomer, etc.
  3. BancomerApiConnectionError. Este tipo de errores son generados cuando la 
     librería intenta contectarse al API, por ejemplo: timeouts, errores de 
     servidores de dominio, etc.
  4. BancomerApiAuthError. Se trata de errores de autenticación generados al 
     momento de contarse al servidor Bancomer, o bien, configuración incorrecta 
     de las credenciales de autenticación (Merchant ID y Api Key).
  5. BancomerApiError. Errores genéricos cuando se procesa una petición. Esta es
     la categoría de errores más general.

Todos los errores ponen disponibles el acceso a la información del error que
regrese el API:

  1. getDescription(): descripción del error generado por Bancomer.
  2. getErrorCode(): código de error generado por Bancomer. Cuando hay errores de
  3. petición o antes de generarla, este valor es igual a cero.
  4. getCategory(): categoría de error generada por Bancomer. Cuando hay errores
     de petición o antes de generarla, este valor es igual a cadena vacía.
  5. getHttpCode(): código del error HTTP generado en la petición al API. Cuando
     hay errores de petición o antes de generarla, este valor es igual a cero.
  6. getRequestId(): código generado cuando se generó el error en el API. Cuando
     hay errores de petición o antes de generarla, este valor es igual a cadena
     vacía.

La implementación de errores se puede ejemplificar de la siguiente manera:

try {
	Bancomer::setId('moiep6umtcnanql3jrxp');
	Bancomer::setApiKey('sk_'); // esta línea generará un error debido a que el 
	                           // formato de la llave privada es incorrecto
	Bancomer::setSandboxMode(true);
	
	$bancomer = Bancomer::getInstance();
	$customer = $bancomer->customers->get('a9ualumwnrcxkl42l6mh');
 	$customer->name = 'Juan';
 	$customer->last_name = 'Godinez';
 	$customer->save();
} catch (BancomerApiTransactionError $e) {
	error('ERROR en la transacción: ' . $e->getMessage() . 
	      ' [código de error: ' . $e->getErrorCode() . 
	      ', categoría de error: ' . $e->getCategory() . 
	      ', código HTTP: '. $e->getHttpCode() . 
	      ', id petición: ' . $e->getRequestId() . ']');

} catch (BancomerApiRequestError $e) {
	error('ERROR en la petición: ' . $e->getMessage());

} catch (BancomerApiConnectionError $e) {
	error('ERROR en la conexión al API: ' . $e->getMessage());

} catch (BancomerApiAuthError $e) {
	error('ERROR en la autenticación: ' . $e->getMessage());
	
} catch (BancomerApiError $e) {
	error('ERROR en el API: ' . $e->getMessage());
	
} catch (Exception $e) {
	error('Error en el script: ' . $e->getMessage());
}

EJEMPLOS:


Clientes

Agregar Cliente:
	$customerData = array(
		'name' => 'Teofilo',
		'last_name' => 'Velazco',
		'email' => 'teofilo@payments.com',
		'phone_number' => '4421112233',
		'address' => array(
			'line1' => 'Av. 5 de Febrero No. 1',
			'line2' => 'Col. Felipe Carrillo Puerto',
			'line3' => 'Zona industrial Carrillo Puerto',
			'postal_code' => '76920',
			'state' => 'Querétaro',
			'city' => 'Querétaro',
			'country_code' => 'MX'));

	$bancomer = Bancomer::getInstance();
 	$customer = $bancomer->customers->add($customerData);

Obtener Cliente:
	$bancomer = Bancomer::getInstance();
	$customer = $bancomer->customers->get('a9ualumwnrcxkl42l6mh');
	
Obtener listado de Clientes:
	$findData = array(
		'creation[gte]' => '2013-01-01',
		'creation[lte]' => '2013-12-31',
		'offset' => 0,
		'limit' => 5);
	$bancomer = Bancomer::getInstance();
 	$customerList = $bancomer->customers->getList($findData);
 	
Actualización de datos del Cliente:
	$bancomer = Bancomer::getInstance();
	$customer = $bancomer->customers->get('a9ualumwnrcxkl42l6mh');
 	$customer->name = 'Juan';
 	$customer->last_name = 'Godinez';
	$customer->save();
	
Eliminar Cliente:
	$bancomer = Bancomer::getInstance();
	$customer = $bancomer->customers->get('a9ualumwnrcxkl42l6mh');
	$customer->delete();
	
Cargos:

Hacer cargo a Merchant:
	$chargeData = array(
		'method' => 'card',
		'source_id' => 'krfkkmbvdk3hewatruem',
		'amount' => 100,
		'description' => 'Cargo inicial a mi merchant',
		'order_id' => 'ORDEN-00071');
	$bancomer = Bancomer::getInstance();
	$charge = $bancomer->charges->create($chargeData);
	debug($charge);

Obtener cargo de Cliente:
	$bancomer = Bancomer::getInstance();
	$customer = $bancomer->customers->get('a9ualumwnrcxkl42l6mh');
	$charge = $customer->charges->get('tvyfwyfooqsmfnaprsuk');
	
Obtener cargo de Merchant:
	$bancomer = Bancomer::getInstance();
	$charge = $bancomer->charges->get('tvyfwyfooqsmfnaprsuk');
	
Hacer devolución de cargo a Merchant:
	$refundData = array(
		'description' => 'Devolución' );
	$bancomer = Bancomer::getInstance();
	$charge = $bancomer->charges->get('tvyfwyfooqsmfnaprsuk');
	$charge->refund($refundData);

Token:

Crear token de Merchant:
	$tokenData = array(
		'trial_days' => '90',
		'plan_id' => 'pduar9iitv4enjftuwyl',
		'card_id' => 'konvkvcd5ih8ta65umie');
	$bancomer = Bancomer::getInstance();
	$token = $bancomer->tokens->add($tokenData);
	
Obtener token de Merchant:
	$bancomer = Bancomer::getInstance();
	$token = $bancomer->tokens->get('a9ualumwnrcxkl42l6mh');