Dev (Back & Front)ARTIGO

Recebendo notificações instantâneas de pagamento

Nas últimas postagens, temos falado sobre a integração com as APIs de pagamento PayPal. Hoje vamos falar sobre um outro tipo de API do PayPal que pode ser de extrema importância para vários modelos de negócio: a Notificação de Pagamento Instantâneo (ou IPN da sigla em inglês). A IPN é um sistema de mensagens que avisará sua aplicação sobre modificações de status em transações, o que é muito interessante quando temos um back-office que precisa tomar decisões rapidamente. O primeiro passo para integrarmos com o serviço de notificação é habilitar o serviço no PayPal. Para isso, faremos login em nossa conta de vendedor e, na aba “Minha Conta”, vamos até a opção “Mais Opções” dentro de “Perfil”.

Perfil / outras opções

Em seguida, selecionamos a opção “Notificações de Pagamento Instantâneo”, que fica em “Minhas Ferramentas de Venda”:

ipn

Depois, definimos a URL que receberá as notificações:

URL ipn

Bom, a partir desse momento, o PayPal enviará notificações para a URL que foi definida, porém precisaremos manipular essas notificações. Para isso, precisamos compreender o funcionamento do sistema de notificações:

  1. Uma transação tem seu estado modificado (um novo cliente efetuou uma compra, uma restituição foi feita etc).
  2. O PayPal enviará uma mensagem para sua URL.
  3. Você receberá a mensagem e a reenviará, exatamente como foi recebida, com os campos contendo os mesmos valores e na mesma sequência de volta para o PayPal.
  4. O PayPal responderá VERIFIED ou INVALID.
  5. Caso a resposta seja VERIFIED, você tomará as decisões segundo suas regras de negócio.

A implementação do observador é bastante simples:

  <?php
/**
 * Observador de Notifica????es de Pagamento Instant??neo
 */
class InstantPaymentNotification {
    /**
     * @var    string
     */
    private $endpoint = 'https://www.paypal.com';

    /**
     * @var    IPNHandler
     */
    private $ipnHandler;

    /**
     * Constroi o objeto que receber?? as notifica????es de pagamento
     * instant??neas do PayPal..
     * @param    boolean $sandbox Define se ser?? utilizado o Sandbox
     * @throws    InvalidArgumentException
     */
    public function __construct( $sandbox = false ) {
        if ( !!$sandbox ) {
            $this->endpoint = 'https://www.sandbox.paypal.com';
        }

        $this->endpoint .= '/cgi-bin/webscr?cmd=_notify-validate';
    }

    /**
     * Aguarda por notifica????es de pagamento instant??nea; Caso uma nova
     * notifica????o seja recebida, faz a verifica????o e notifica um manipulador
     * com o status (verificada ou n??o) e a mensagem recebida.
     * @see        InstantPaymentNotification::setIPNHandler()
     * @throws    BadMethodCallException Caso o m??todo seja chamado antes
     * de um manipulador ter sido definido ou nenhum email de recebedor
     * tenha sido informado.
     */
    public function listen() {
        if ( $this->ipnHandler !== null && count( $this->receiverEmail ) >= 1 ) {
            if ( $_SERVER[ 'REQUEST_METHOD' ] == 'POST' ) {
                if ( filter_input( INPUT_POST , 'receiver_email' , FILTER_VALIDATE_EMAIL ) ) {
                    $curl = curl_init();

                    curl_setopt( $curl , CURLOPT_URL , $this->endpoint );
                    curl_setopt( $curl , CURLOPT_SSL_VERIFYPEER , false );
                    curl_setopt( $curl , CURLOPT_RETURNTRANSFER , 1 );
                    curl_setopt( $curl , CURLOPT_POST , 1 );
                    curl_setopt( $curl , CURLOPT_POSTFIELDS , http_build_query( $_POST ) );

                    $response = curl_exec( $curl );
                    $error = curl_error( $curl );
                    $errno = curl_errno( $curl );

                    curl_close( $curl );

                    if ( empty( $error ) && $errno == 0 ) {
                        $this->ipnHandler->handle( $response == 'VERIFIED' , $_POST );
                    }
                }
            }
        } else {
            throw new BadMethodCallException( 'Nenhum manipulador de mensagem ou email foi definido' );
        }
    }

    /**
     * Define o objeto que irá manipular as notificações de pagamento
     * instantâneas enviadas pelo PayPal.
     * @param    IPNHandler $ipnHandler
     */
    public function setIPNHandler( IPNHandler $ipnHandler ) {
        $this->ipnHandler = $ipnHandler;
    }
}

Essa classe é reutilizável, independentemente das regras de negócio da sua aplicação – tudo o que ela faz é receber a notificação, verificá-la no PayPal e avisar um manipulador sobre o evento. Para definir o manipulador, utilizaremos a seguinte interface:

  <?php
/**
 * Interface para definição de um manipulador de Notificação
 * de Pagamento Instantânea.
 */
interface IPNHandler {
    /**
     * Manipula uma notificação de pagamento instantânea recebida
     * pelo PayPal.
     * @param    boolean $isVerified Identifica que a mensagem foi
     * verificada como tendo sido enviada pelo PayPal.
     * @param    array $message Mensagem completa enviada pelo
     * PayPal.
     */
    public function handle( $isVerified , array $message );
}

Como podemos ver, o método handle() da interface IPNHandler receberá dois parâmetros:

  1. isVerified
  2. $message

O parâmetro isVerified informará se é uma mensagem válida. Esse parâmetro deve ser checado sempre que uma notificação for recebida para se evitar fraudes. O segundo parâmetro, message, contém exatamente a mensagem enviada pelo PayPal e deverá ser manipulada segundo suas regras de negócio. Para criar um manipulador para um caso específico, basta que implementemos a interface:

  <?php
/**
 * Manipulador de exemplo de Notificação de Pagamento
 * Instantâneo
 */
class SampleIPNHandler implements IPNHandler {
    /**
     * @param    boolean $isVerified
     * @param    array $message
     * @see        IPNHandler::handle()
     */
    public function handle( $isVerified , array $message ) {
        /**
         * Verificamos se foi realmente o PayPal quem enviou a
         * notificação
         */
        if ( $isVerified ) {
            /**
             * Verificamos se o email contido no campo receiver_email
             * é o nosso email; Essa é uma segunda verificação necessária.
             */
            if ( $message[ 'receiver_email' ] == 'meu-email@dominio.com' ) {
                // está tudo ok, seguimos com nossas regras de negócio
                // podemos enviar um email para alguém, emitir nota fiscal
                // eletrônica se for o caso ou qualquer outra decisão
                // relacionada com as regras de negócio.
            }
        }
    }
}

Agora é só criar o arquivo que receberá as notificações:

  <?php
$ipn = new InstantPaymentNotification( true );
$ipn->setIPNHandler( new SampleIPNHandler() );
$ipn->listen();

E é isso. No próximo artigo, vamos ver como criar um manipulador que enviará a notificação diretamente para nossos celulares ou tablets no exato momento em que o PayPal enviar a notificação.

é engenheiro de aplicações e trabalha com ambiente web desde 2000 em diversas linguagens, como Java e PHP, dedicando esforços ao desenvolvimento de bibliotecas reutilizáveis para a comunidade. Especialista em integração de sistemas, possui várias bibliotecas reutilizáveis publicadas como open-source para a comunidade, como biblioteca Cielo, PayPal, ECT (Correios), BuscaPé, Lomadee, Twitter, Facebook entre várias outras. É administrador do fórum iMasters e iMasters Code, onde compartilha conhecimento com a comunidade de desenvolvedores. Também é autor de cursos no iMasters PRO.

Ver perfil