[PagSeguro] Faça log do retorno automático
Você deve conhecer o PagSeguro, já venho falando dele por aqui há um tepinho. Existe uma biblioteca de retorno automático para PHP gerada pela Visie, só que o pessoal geralmente tem dúvidas sobre verificar o que o PaSeguro ou quando o PagSeguro irá mandar os dados para validação.
O código básico de implementação é este:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | // Aqui vai seu Token define('TOKEN','0123456789abcdef0123456789abcdef'); // Incluindo o arquivo da biblioteca include('retorno.php'); // Função que captura os dados do retorno function retorno_automatico ( $VendedorEmail, $TransacaoID, $Referencia, $TipoFrete, $ValorFrete, $Anotacao, $DataTransacao, $TipoPagamento, $StatusTransacao, $CliNome, $CliEmail, $CliEndereco, $CliNumero, $CliComplemento, $CliBairro, $CliCidade, $CliEstado, $CliCEP, $CliTelefone, $produtos, $NumItens) { // AQUI VOCÊ TEM OS DADOS RECEBIDOS DO PAGSEGURO, JÁ VERIFICADOS. // CONFIRA A LISTA DE PRODUTOS E O VALOR COM O QUE VOCÊ TEM NO // BANCO DE DADOS E, SE ESTIVER TUDO CERTO, ATUALIZE O STATUS // DO PEDIDO. } // A partir daqui, é só HTML: |
Como você pode ver, você escreve uma função básica onde você fará tudo o que você precisa fazer no seu banco de dados, como atualizar a quantidade de produtos de uma loja, cancelar ou efetuar um pedido.
Normalmente o pessoal pergunta, como ver o que o PagSeguro está mandando, como fazer para escrever um código não vendo o que está vindo. Bem, você não vai ver nunca o que está acontecendo na tela do seu navegador. Para você ver o que está acontecendo, escreva em um arquivo de texto os dados que estão chegando no seu POST e também se ele foi validado ou não.
Vamos definir uma função de Log bem simples.
1 2 3 4 5 | function pgs_log($msg) { $msg = date('[d/m/Y H:i:s] ') . $msg . "\n\n---\n"; file_put_contents ('pagseguro.log', $msg); } |
Agora, vamos implementar o nosso log.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | function pgs_log($msg) { $msg = date('[d/m/Y H:i:s] ') . $msg . "\n\n---\n"; file_put_contents ('pagseguro.log', $msg); } if ($_POST) { pgs_log('Recebendo dados via POST, estes dados serão verificados pelo PagSeguro: '.print_r($_POST, true)); } // Aqui vai seu Token define('TOKEN','0123456789abcdef0123456789abcdef'); // Incluindo o arquivo da biblioteca include('retorno.php'); // Função que captura os dados do retorno function retorno_automatico ( $VendedorEmail, $TransacaoID, $Referencia, $TipoFrete, $ValorFrete, $Anotacao, $DataTransacao, $TipoPagamento, $StatusTransacao, $CliNome, $CliEmail, $CliEndereco, $CliNumero, $CliComplemento, $CliBairro, $CliCidade, $CliEstado, $CliCEP, $CliTelefone, $produtos, $NumItens) { $data = func_get_args(); pgs_log('Dados verificados com sucesso! Dados formatados no retorno: '.print_r($data, true)); // AQUI VOCÊ TEM OS DADOS RECEBIDOS DO PAGSEGURO, JÁ VERIFICADOS. // CONFIRA A LISTA DE PRODUTOS E O VALOR COM O QUE VOCÊ TEM NO // BANCO DE DADOS E, SE ESTIVER TUDO CERTO, ATUALIZE O STATUS // DO PEDIDO. } |
E voilà! Você verá no seu arquivo pagseguro.log o seguinte resultado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | [19/06/2009 16:53:08] Recebendo dados via POST, estes dados serão verificados pelo PagSeguro: Array
(
[TransacaoID] => e9b6ef2f4b0f873e6f2a5831d39d803b
[TipoFrete] => FR
[ValorFrete] => 0,00
[Anotacao] => Pagamento gerado pelo ambiente de testes
[DataTransacao] => 19/06/2009
[VendedorEmail] => email_cobranca@teste.gov.br
[Referencia] => AFB-580
[CliNome] => nome
[CliEmail] => email@cliente.com.br
[CliEndereco] => Rua dos Bobos
[CliNumero] => 0
[CliComplemento] =>
[CliBairro] => Paytown
[CliCidade] => Payland
[CliEstado] => AC
[CliCEP] => 01234567
[CliTelefone] => 99 55555555
[NumItens] => 4
[ProdID_1] => A27
[ProdDescricao_1] => Mapa da cidade
[ProdQuantidade_1] => 1
[ProdFrete_1] => 0,00
[ProdExtras_1] => 0,00
[ProdValor_1] => 27,90
[ProdID_2] => B720
[ProdDescricao_2] => Bala Freegells
[ProdQuantidade_2] => 12
[ProdFrete_2] => 0,00
[ProdExtras_2] => 0,00
[ProdValor_2] => 0,80
[ProdID_3] => A90
[ProdDescricao_3] => Caderno decorado
[ProdQuantidade_3] => 5
[ProdFrete_3] => 0,00
[ProdExtras_3] => 0,00
[ProdValor_3] => 16,30
[ProdID_4] => C230
[ProdDescricao_4] => Tomada simples
[ProdQuantidade_4] => 16
[ProdFrete_4] => 0,00
[ProdExtras_4] => 0,00
[ProdValor_4] => 1,70
[TipoPagamento] => Pagamento
[StatusTransacao] => Completo
)
---
[19/06/2009 16:53:08] Dados verificados com sucesso! Dados formatados no retorno: Array
(
[VendedorEmail] => email_cobranca@teste.gov.br
[TransacaoID] => e9b6ef2f4b0f873e6f2a5831d39d803b
[Referencia] => AFB-580
[TipoFrete] => FR
[ValorFrete] => 0.00
[Anotacao] => Pagamento gerado pelo ambiente de testes
[DataTransacao] => 19/06/2009
[TipoPagamento] => Pagamento
[StatusTransacao] => Completo
[CliNome] => nome
[CliEmail] => email@cliente.com.br
[CliEndereco] => Rua dos Bobos
[CliNumero] => 0
[CliComplemento] =>
[CliBairro] => Paytown
[CliCidade] => Payland
[CliEstado] => AC
[CliCEP] => 01234567
[CliTelefone] => 99 55555555
[produtos] => Array
(
[0] => Array
(
[ProdID] => A27
[ProdDescricao] => Mapa da cidade
[ProdValor] => 27.9
[ProdQuantidade] => 1
[ProdFrete] => 0
[ProdExtras] => 0
)
[1] => Array
(
[ProdID] => B720
[ProdDescricao] => Bala Freegells
[ProdValor] => 0.8
[ProdQuantidade] => 12
[ProdFrete] => 0
[ProdExtras] => 0
)
[2] => Array
(
[ProdID] => A90
[ProdDescricao] => Caderno decorado
[ProdValor] => 16.3
[ProdQuantidade] => 5
[ProdFrete] => 0
[ProdExtras] => 0
)
[3] => Array
(
[ProdID] => C230
[ProdDescricao] => Tomada simples
[ProdValor] => 1.7
[ProdQuantidade] => 16
[ProdFrete] => 0
[ProdExtras] => 0
)
)
[NumItens] => 4
)
--- |
E com isso você já consegue desenvolver sua aplicação normalmente. Fora que fica mais prático de entender o que está acontecendo no seu servidor.
Função clone da jQuery e atributo name para IE
Aqui na ACSP, onde estamos desenvolvendo um mega-hiper-ultra-plus-and-is-the-maximun-software-of-the-solar-system sistema ultra-secreto nos deparamos com um problema cabuloso no Browser azul até a versão 7. A idéia é a seguinte: quando um usuário clicar em um determinado botão “adicionar uma cópia” do formulário ele deve copiar o fieldset anterior e colá-lo abaixo do mesmo. Obviamente, devemos alterar o name dos campos para conseguir tratá-los no PHP no server-side. Atente a este ponto, todas as ações aplicadas aos campos devem continuar funcionando, ou seja, se você aplicou um “click”, “change” “blur” ou seja lá o que for, deve continuar funcionando normalmente. Ah sim, vamos utilizar a biblioteca “coisinha bonitinha do papai” jQuery.
A melhor forma de resolver esse problema é pensar antes de escrever o código. Mas algumas vezes não conseguimos prever coisas que nem a Microsoft explica. Então o ideal é fazer uma função que aplique as ações ao formulário assim, podemos usar um template para fazer o clone.
1 2 3 4 5 6 7 8 9 10 11 12 13 | $(function(){ aplica_acoes(); $('button.duplicar').click('duplica_fieldset'); }); function duplica_fieldset() { $('fieldset:last').after ('------------ cole aqui o template -----------'); aplica_acoes(); } function aplica_acoes() { $('.campo').click(function (){alert("Hey! Ho!");}); } |
O problema disso é ao alterarmos qualquer campo teriamos que alterar o javascript para que o template fique exatamente igual. Outro problema é que ao chamarmos a função aplica_funcoes ele adicionará duas vezes a ação click no campo.
Mas vamos por partes, primeiro o problema do click duplicado. Podemos resolver isso sem problema algum. Basta usar o unbind e bind.
11 12 13 14 15 16 17 18 19 | function aplica_funcoes() { $('.campo') .unbind('click', heyho) .bind('click', heyho); } function heyho() { alert("Hey! Ho!"); } |
Legal, o que isso faz é remover os eventos click e recoloca-os. Muito bem, também poderiamos usar o live, adicionado na jQuery desde a versão 1.3.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | $(function(){ aplica_acoes(); $('button.duplicar').click('duplica_fieldset'); }); function duplica_fieldset() { $('fieldset:last').after ('------------ cole aqui o template -----------'); } function aplica_funcoes() { $('.campo').live('click', heyho); } function heyho() { alert("Hey! Ho!"); } |
Assim, todos os campos que forem criados após essa chamada do live, todas as vezes que um elemento com a classe .campo for criado ele já nascerá com a ação click.
Muy biem, compañeros! Vamos ao próximo problema. O template que não deve ficar aqui no javascript e sim clonar o dito cujo. Para isso vamos usar o método clone da jQuery, assim ele copiará o código escrito no próprio html, assim não precisamos dar manutenção no código duas vezes. Veja como é simples usar o clone.
6 7 8 9 | function duplica_fieldset() {
var fls = $('fieldset:last').clone();
$('fieldset:last').after (fls);
} |
Ok, com isso já podemos copiar um fieldset e colar logo abaixo do outro fieldset. Uma coisa muito interessante é que podemos passar o parametro true dentro do clone, assim ele já copia os eventos, fazendo essa alteração nosso script ficará mais ou menos assim (perceba como diminui a quantidade de código).
1 2 3 4 5 6 7 8 9 10 11 12 13 | $(function(){ aplica_acoes(); $('button.duplicar').click('duplica_fieldset'); }); function duplica_fieldset() { var fls = $('fieldset:last').clone(true); $('fieldset:last').after (fls); } function aplica_funcoes() { $('.campo').click(function(){alert("Hey! Ho!");}); } |
E o name?
Agora, vamos ao problema maior, vamos alterar o name para podermos trabalhar no server-side. Tomemos o seguinte template de html:
1 2 3 4 5 | <fieldset> <label for="nome_123">Nome <input type="text" name="nome_123" id="nome_123" /></label> <label for="campo_123">Nome <input type="text" name="campo_123" id="campo_123" class="campo" /></label> </fieldset> <button class="duplicar">Nova Assinatura</button> |
Perceba que usamos o padrão “123″ em todos os campos, seria o nosso ID temporário para podermos tratar sem problemas no server-side. Então, para alterar o campo deveriamos criar um número aleatório único e alterar via comando attr da jQuery. Algo como isto:
6 7 8 9 10 11 12 13 14 15 16 | function duplica_fieldset() { var num = ''+(new Date().getTime())+(parseInt(Math.random()*100)); var fls = $('fieldset:last').clone(true); $('[name]', fls).each(function(){ var lastName = this.name; var base = lastName.split('_')[0]; var newName = base+'_'+num; this.name=newName; }); $('fieldset:last').after (fls); } |
Veja, para criar um número aleatório único usei o getTime do objeto Date, assim pegamos os microsegundos que aconteceram naquele momento, um momento único que não se repetirá. E então adicionamos a um número aleatório qualquer. O Math.random() gera um número aleatório entre zero e um, então é necessário multiplicá-lo com um valor multiplo de dez para termos o inteiro desejado. Então usamos o parseInt para converter esse float maluco para inteiro e obtermos apenas o desejado. Perceba que no inicio dessa linha adicionamos uma string vazia, isso para que os dois valores não sejam somados e sim concatenados.
Veja, que logo após chamamos todos os campos que tenham o campo name, isso apenas no nosso fls, clonado anteriormente. Podemos fazer isso com todos os atributos (id, for, class, etc). Aconselho a fazer um each só para não deixar sua aplicação lenta.
Tá, mas qual é o problema com o Internet Explorer?
Tudo bem? Tudo funcionando perfeitamente? Tudo tranquilo? Sim, com apenas um problema. No Internet Explorer. Até a versão sete esse problema existia, mas na versão oito o problema foi corrigido. O problema é o seguinte: EM RADIO BUTTONS E CHECKBOXES O NAME NÃO PODE SER ALTERADO DINAMICAMENTE VIA JAVSCRIPT! Aí já viu, né? Eles (os desenvolvedores do Internet Explorer) devem ter feito isso por que se alterar o name destes tipos de campos acabará com o grupo já instituido.
Então, o nosso código funciona perfeitamente em browsers. Então aquela história de escrever o template no JavaScript é a forma de resolver. Sim, é uma forma, o problema é que algumas vezes só nos deparamos com problemas quando a tela já está cheia de detalhes. Então o que temos que fazer é tentar resolver de outra forma, mais simples.
Vendo o código-fonte da jQuery, percebi que a função clone já faz um hack para IE, devido à forma como o dito cujo faz cópia com o comando cloneNode. Usando o método cloneNode do DOM, o Browser da Microsoft faz um clone dos eventos, então, se você remover o evento de um, ele remove o evento de todos ao mesmo tempo. Veja o trecho onde o clone é definido na jQuery.
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 | clone: function( events ) { // Do the clone var ret = this.map(function(){ if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) { // IE copies events bound via attachEvent when // using cloneNode. Calling detachEvent on the // clone will also remove the events from the orignal // In order to get around this, we use innerHTML. // Unfortunately, this means some modifications to // attributes in IE that are actually only stored // as properties will not be copied (such as the // the name attribute on an input). var html = this.outerHTML; if ( !html ) { var div = this.ownerDocument.createElement("div"); div.appendChild( this.cloneNode(true) ); html = div.innerHTML; } return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g, "").replace(/^\s*/, "")])[0]; } else return this.cloneNode(true); }); // Copy the events from the original to the clone if ( events === true ) { var orig = this.find("*").andSelf(), i = 0; ret.find("*").andSelf().each(function(){ if ( this.nodeName !== orig[i].nodeName ) return; var events = jQuery.data( orig[i], "events" ); for ( var type in events ) { for ( var handler in events[ type ] ) { jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data ); } } i++; }); } // Return the cloned set return ret; }, |
Bom, já que ele já hackeia, decidi fazer um plugin que seja igual ao clone só que interferindo nesse hack. Eis o código final.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | /** * Extensão para jQuery clonar um elemento para corrigir o BUG do IE */ $.fn.clonar = function( events , manipulateForIE) { // Do the clone var ret = this.map(function(){ if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) { var html = this.outerHTML; if ( !html ) { var div = this.ownerDocument.createElement("div"); div.appendChild( this.cloneNode(true) ); html = div.innerHTML; } // Isto foi adicionado à função de clonar if (manipulateForIE != undefined && $.isFunction(manipulateForIE)) html=manipulateForIE(html) return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g, "").replace(/^\s*/, "")])[0]; } else return this.cloneNode(true); }); // Copy the events from the original to the clone if ( events === true ) { var orig = this.find("*").andSelf(), i = 0; ret.find("*").andSelf().each(function(){ if ( this.nodeName !== orig[i].nodeName ) return; var events = jQuery.data( orig[i], "events" ); for ( var type in events ) for ( var handler in events[ type ] ) jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data ); i++; }); } // Return the cloned set return ret; } |
Daí, basta passar uma função que interfirirá no meio do clone da jQuery clonar. O código deve ficar assim:
8 9 10 | var fls=$('fieldset.'+classe+':last').clonar(true, function(html){ return html.replace(/name="?(\w+)_\d+/ig, 'name="$1_'+r+'" '); }); |
E este é o nosso código final, já com o plugin e tudo o que tem direito:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | $(function(){ aplica_acoes(); $('button.duplicar').click('duplica_fieldset'); }); function duplica_fieldset() { var fls = $('fieldset.'+classe+':last').clonar(true, function(html){ return html.replace(/name="?(\w+)_\d+/ig, 'name="$1_'+r+'" '); }); $('fieldset:last').after (fls); } function aplica_funcoes() { $('.campo').click(function(){alert("Hey! Ho!");}); } /** * Extensão para jQuery clonar um elemento para corrigir o BUG do IE */ $.fn.clonar = function( events , manipulateForIE) { // Do the clone var ret = this.map(function(){ if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) { var html = this.outerHTML; if ( !html ) { var div = this.ownerDocument.createElement("div"); div.appendChild( this.cloneNode(true) ); html = div.innerHTML; } // Isto foi adicionado à função de clonar if (manipulateForIE != undefined && $.isFunction(manipulateForIE)) html=manipulateForIE(html) return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g, "").replace(/^\s*/, "")])[0]; } else return this.cloneNode(true); }); // Copy the events from the original to the clone if ( events === true ) { var orig = this.find("*").andSelf(), i = 0; ret.find("*").andSelf().each(function(){ if ( this.nodeName !== orig[i].nodeName ) return; var events = jQuery.data( orig[i], "events" ); for ( var type in events ) for ( var handler in events[ type ] ) jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data ); i++; }); } // Return the cloned set return ret; } |
Uma vez o professor me disse: “Não tenha medo do código.” E isso eu passo para todo mundo que eu converso. Como você pode ver, o código da jQuery é bem escrito e bem documentado, pare e leia alguma coisa para você aprender cada vez mais. Ah, e caso você precise de um lugar de consulta para o dia-a-dia você pode usar o JavaScript Cheat Sheet.
PHP+PagSeguro = Loja virtual de downloads
Voltamos a um assunto que interessa muitas pessoas: ganhar dinheiro. Talvez você não tenha nada físico para vender, mas você provavelmente tem talento (não, chocolate não) e sabe escrever um bom livro ou mesmo fazer ótimas fotografias. Você pode não saber, mas dá pra ganhar dinheiro com esse seu talento. Por que você não começa a vender arquivos para download? Sim! Por que não?! Comece a explorar o seu lado criativo e faça de seus dons uma forma de fazer lucros.
Crie uma loja virtual. Você pode começar com uma loja poderosa como o Magento ou mesmo uma coisa mais simples como OsCommerce, Joomla o mesmo WordPress. Mas, se você é programador como eu e prefere escrever seu próprio código ou mesmo entender como o sistema de sua loja (ou loja de seus clientes) funcionará, farei uma série de artigos para demonstrar como é simples criar uma loja virtual cujos produtos não são nada além de links para download.
Para você conseguir acompanhar este “curso” você deve entender um pouco dos seguintes assuntos:
- PagSeguro: Vamos usar esta ferramenta de pagamento online para que nossos clientes se sintam seguros ao comprar em sua loja, então você já pode criar sua conta para podermos fazer a nossa loja virtual
- PHP: Não precisa ser um expert, mas saber conceitos básicos de banco de dados e orientação a objetos já dão um gás
- MySQL: Vamos trabalhar com a dobradinha mais conhecida da Web. Não usaremos o MySQL ao extremo mas iremos fazer um join ou outro
- Ânimo: muitas vezes você irá errar. Podem ser erros básicos como não colocar um ponto-e-virgula/ no seu script, então não desanime e continue em frente
Ok, com os requisitos em mãos vamos por a mão na massa. A começar pela estruturação do nosso banco de dados.
O banco de dados
Vamos precisar basicamente de duas tabelas para organizar os produtos: categorias, produtos. Elas são descritas pelas seguintes queries:
CREATE TABLE `categoria` ( `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, `slug` VARCHAR(45) NOT NULL, `titulo` VARCHAR(100) NOT NULL, PRIMARY KEY (`id`) ); CREATE TABLE `produto` ( `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, `categoria_id` INTEGER UNSIGNED NOT NULL, `slug` VARCHAR(45) NOT NULL, `titulo` VARCHAR(100) NOT NULL, `descricao` TEXT NOT NULL, `valor` FLOAT NOT NULL, `url` VARCHAR(32) NOT NULL, `imagem` CHAR(32) NOT NULL, PRIMARY KEY (`id`) );
Usaremos os campos “slug” tanto de categoria quanto de produto para a url na loja virtual ficar mais fácil de enteder, é o que chamamos de URLs amigáveis. Repare também que temos “categoria_id” na tabela de produto, é nela que vamos basear a nossa organização, um produto deve ter uma categoria e uma categoria pode ter diversos produtos. Os campos de url e imagem da tabela de produtos conterá o endereço do arquivo físico (o arquivo que o usuário fará o download) e a imagem que aparecerá na loja virtual respectivamente. Eles receberão uma criptografia para a segurança de sua loja, usarei md5 como criptografia.
Muito bem, não vamos nos prender a criação de usuários no nosso sistema. Por quê?! Porque você realmete não precisa disso na sua loja. Se um usuário compra de sua loja ele terá os seus dados no PagSeguro, afinal, ele vai pagar por lá não é mesmo? Então para quê ter dados repetidos no seu sistema? Não vamos fazer também um sistema de login, tenho em mente que você é um programador experto e já sabe fazer isso de olhos fechados. Vamos colocar um sistema de login no nosso sistema sim, claro, mas com um usuário único e uma senha única. Isso para não perdermos o foco da loja virtual.
A outra tabela que iremos precisar é a tabela de compra, nela teremos a compra de cada usuário, assim poderemos reenviar um e-mail ao usuário com os links para onde ele pode efetuar o download dos produtos que ele comprou. Esta tabela também serve para sabermos que o produto foi enviado ao cliente, digo, a data de envio do e-mail. Essa tabela é regida pelo seguinte SQL.
CREATE TABLE `compra` ( `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, `email` VARCHAR(100), `produtos` TEXT NOT NULL, `transacao_id` VARCHAR(45), `status` VARCHAR(45) NOT NULL, `data_confirmacao` DATETIME NOT NULL, PRIMARY KEY (`id`) );
Veja que produtos é um campo do tipo do tipo TEXT, nele vamos gravar os ids contidos na tabela produto, separados por virgula. Os campos transacao_id, status e data_confirmacao serão completados ao receber o retorno do PagSeguro.
Com isso já temos as tabelas básicas necessárias para a criação de nossa loja virtual. Fique ligado para a próxima parte dessa série onde vamos montar uma loja virtual voltada para downloads.
Pink Floyd Cover via @zamana
É, esta semana a animação rolou por conta do meu amigo Rafael @rzamana, acontece que o irmão dele veio passar alguns dias aqui em SP e como não poderia deixar de ser, a animação não pôde faltar. Ele procurou um bom lugar para ir e acabou encontrando um ótimo bar onde teria show da banda Pink Floyd Cover.
Algumas batidas de foto, umas doses de José Cuervo e a noite já estava no ponto. A noite foi regada ao som de uma banda que faz muito bem o seu trabalho. O som dos caras é muito arecido ao som dos pessoal da banda inglesa. Uma noite tranquila e muita agitação. Fica aqui também registrada a entrada de mais um membro na equipe PV: Luiza que está ajudando Alice com a programação front end.
WordPress fora da pasta raíz
Já teve vontade de xingar o WordPress por criar aquele monte de arquivos na home do seu site, dificultando assim a organização dos seus arquivos dentro do servidor. Bom, neste vídeo eu explico como fazer para colocar o WordPress do seu blog
para rodar dentro de uma pasta e não na raíz do projeto
. Assim você ter uma organização melhor dos seus arquivos.
WordPress: Adicionando um arquivo externo após o primeiro Post
Como fazer para meu blog mostrar um video do CampusOnline ou qualquer outra coisa apenas após o primeiro Post do meu blog?
Muito simples, basta adicionar uma variável que contará os seus posts mostrados (no nosso caso $contador) dentro da sua estrutura the loop. Se o $contator for igual ou menor que 1 mostre o que você deseja. Veja o código:
1 2 3 4 5 6 7 | <?php $contador=0; if (have_posts()) : while (have_posts()) : the_post(); $contador++; ?> // seu loop <?php if ($contador <= 1) { include (STYLESHEETPATH . '/seu-arquivo.php'); } ?> <?php endwhile; ?> <?php else : ?> <?php endif; ?> |
Simples, né? Este é um artigo inspirado no “Adding a external file after the first post” do “WPCult“.
Rã descoberta no Peru tem cerca de 1 centímetro
Momento animal, estava vendo o meu RSS e acabei deparando com esta reportagem do Terra que achei muito interessante para postar aqui no meu Blog. Chega de rodeios, essa é a matéria na íntegra:
Uma nova espécie de rã foi encontrada no Parque Nacional Manu, perto de Cuzco, no sudeste do Peru. A descoberta foi anunciada pelo Instituto de Pesquisa e Museu de História Natural de Dresden. As informações são da agência AFP.
A espécie foi batizada de Noblella pygmaea e é a menor encontrada nos Andes, com cerca de 1 cm de comprimento. A rã é um dos menores vertebrados já encontrados acima de 3 mil metros de altitude e vive em ambientes úmidos, mas terrestres.
Edgar Lehr do Museu de História Natural de Dresden, na Alemanha, e Alessandro Catenazzi da Universidade da Califórnia, em Berkeley, afirmam que a Noblella pygmaea é apenas um dos muitos anfíbios ainda desconhecidos que vivem em zona montanhosa dos Andes.
Semana animada \o/
Não sou muito de sair de casa, mas esta semana foi uma semana em que me dediquei bastante ao lazer e à cultura. Desde teatro até festa de hip-hop. Mas, vamos por parte e vejam como esta semana foi interessante.
Semana passada (exatamente) fomos a uma peça de teatro chamada “A vida secreta de Batman e Robin“. Uma peça muito boa que conta a vida íntima do meu super-heroi favorito. Em suma o arqui-vilão “Charada” e a “mulher-gato” espalharam uma poção na caixa d’água do batman. Com isso, eles começam soltar a franga geral.
O espetáclo acontece com o batman dos anos 60, ou seja, fantasia cinza, todos os elementos são “bat-alguma-coisa”. Alguns erros como o batman cair no chão ao tentar dar um rodopio. O pessoal é muito bom com o improvisso e algumas vezes até achei que não era improvisso, mas isso não vem ao caso.
Logo, no meio da semana nosso amigo @rzamana e @paladino fomos à paulista comemorar o fechamento de um contrato. Fomos a um barzinho legal que fica atrás do MASP. Alguns salgadinhos e suco (parei com refrigerante) e fomos para a Starbucks. No mundo do café acabei pedindo um shake de morango. DAMMIT! Mas beleza! Estava tão bom quanto o café (@apocalypse me mataria agora). Quem quiser, pode ver a lista completa de fotos no meu álbum.
Hoje estamos saindo daqui a pouco de volta para a starbucks e indo para uma festa de hip hop. Quem estiver afim, basta dar um pulo na rua Augusta. O preço é 15 contos e mulher não paga nada até meia-noite. Fica aí a dica.
[update] HipHop FAIL. Pois é, como o @zamana contou em seu blog, eu acabei esquecendo de pegar o endereço correto do evento e acabamos indo parar em outra balada. Uma casa de shows chamada Inferno Club. Tocou rock anos 60/70. Não foi um show de hip hop como esperávamos mas foi rock e assim mesmo tá valendo.
O local é muito bom, @alice teve acesso privilegiado à entrada e lá dentro tem local para ela ficar sentada com umas mesinhas. A noite terminou umas quatro da manhã regado a três doses de tequilas e uma passada no Mc Donalds para recuperar as energias. Como estou de regime fiquei por conta do Chicken Crispy.
Campo DataTransacao no retorno do PagSeguro
Algumas vezes algumas pessoas me perguntam sobre o campo DataTransacao quando trabalhamos com boletos? Qual data vem nesse campo? A data em que o boleto foi emitido? A data em que o status foi alterado? Ou a data em que o POST foi enviado para você. (se quiser uma explicação sobre o que é esse tal de POST ou como funciona o PagSeguro, aconselho a ler meu artigo escrito no forum do iMasters)

No PagSeguro, o Boleto você pode gerar boletos para seus clientes de forma segura, mas atente para a data de compensação desse boleto. Nem você nem seu cliente querem erros nessas datas
Claro que isso é uma preocupação muito grande para quem trabalha com créditos que dependem da data em que a pessoa efetuou o pagamento. Cinco dias corridos de acesso a partir do pagamento por exemplo.
Então fui procurar uma resposta em meu log de dados, eis que me deparo com dois casos e verifiquei a seguinte sequencia de pagamentos via boletos (os IDs das tranações foram alterados para segurança dos clientes):
//-
Thu Apr 2 21:04:17 2009
‘StatusTransacao’: ‘Aguardando Pagto’,
‘DataTransacao’: ‘02/04/2009 21:08:58‘,
‘TipoPagamento’: ‘Boleto’,
‘TransacaoID’: ‘XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXA’,
Fri Apr 3 06:27:04 2009
‘StatusTransacao’: ‘Aprovado’,
‘DataTransacao’: ‘02/04/2009 21:08:58‘,
‘TipoPagamento’: ‘Boleto’,
‘TransacaoID’: ‘XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXA’,
-//
//-
Thu Apr 2 11:04:38 2009
‘StatusTransacao’: ‘Aguardando Pagto’,
‘DataTransacao’: ‘02/04/2009 11:09:18‘,
‘TipoPagamento’: ‘Boleto’,
‘TransacaoID’: ‘XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXB’,
Fri Apr 3 06:02:47 2009
‘StatusTransacao’: ‘Aprovado’,
‘DataTransacao’: ‘02/04/2009 11:09:18‘,
‘TipoPagamento’: ‘Boleto’,
‘TransacaoID’: ‘XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXB’,
-//
Como podemos perceber, o campo “DataTransacao” não é alterado e provavelmente se refere à data em que a compra foi efetuada, logo aconselho a alterar a data do seu banco de dados com a data atual do sistema. Em mysql você pode usar NOW() (faz um “SELECT NOW()” para ver o que estou falando).
Abraços, espero que isso retire a dúvida de uma ou duas pessoas =D







