JSR 229: Mobile Payment API - Exemplo

Um novo canal de comércio, o de pagamentos móveis (m-payment)

Empresas como a MTV, a Fox Entertainment, a NBC, a LOreal e a Nike já aceitam pagamentos via mobile PenPal. As operações são feitas via portal WAP da PenPal desenvolvido para mobile payment. O Banco do Brasil e o Google já está entrando na onda.

Exemplo oficial da API

import javax.microedition.lcdui.*;
import javax.microedition.midlet.MIDlet;
import javax.microedition.payment.TransactionListener;
import javax.microedition.payment.TransactionModule;
import javax.microedition.payment.TransactionRecord;
import javax.microedition.payment.TransactionModuleException;

public class MyGame extends MIDlet implements TransactionListener, CommandListener {
  private TransactionModule transModulo;
  private int aNivel = 0;
  private int nbNivel = 0;
  private int MAX_NIVEL = 3;
  private Display tela;
  private Command sair;

  public MyGame(){
    tela = Display.getDisplay(this);
    Form formMain = new Form("GamePay");
    sair = new Command("Exit", Command.EXIT, 1);
    formMain.addCommand(sair);
    formMain.setCommandListener(this);
    tela.setCurrent(formMain);

    try {
      transModulo = new TransactionModule(this);
    }
    catch(TransactionModuleException e) {
      /* O módulo do pagamento indica que recusou uma conexão de TransactionModule. 
      Possíveis motivos: os dados da provisão estão errados ou incompletos ou que há conexões excessivas ligadas a esse módulo de pagamento */
      e.toString();
    }
    catch(Exception e) { }

    try {
      transModulo.setListener(this);
    } catch(Exception e) { }
  }
 
  public void startApp(){
    while (nbNivel &lt= MAX_NIVEL) {
    // pagamento (payment) obrigatório p/ o nível
    try {
      transModulo.process(aNivel, "Prox Nível", "Libera o acesso ao nível seguinte.");
      synchronized(this) {
        try {
          wait(); // esperar até que a rechamada seja feita
        }
        catch (InterruptedException ie) { }
      }
    }
    catch (Exception e) { }
    // início do nível
    Form formLevel = new Form("Nivel " + nbNivel);
    tela.setCurrent(formLevel);
    try {
      Thread.sleep(5000);
    } 
    catch (InterruptedException e1) {
      el.getMessage()
    }
    // fim do nível
    nbNivel++;
    }
  }


  public void processed(TransactionRecord record){
    switch(record.getState()) {
      case TransactionRecord.TRANSACTION_SUCCESSFUL:
      // transação de pagamento bem sucedida
      break;
      case TransactionRecord.TRANSACTION_REJECTED:
      // transação de pagamento mal sucedida, deve-se pagar para jogar
      break;
      case TransactionRecord.TRANSACTION_FAILED:
      default:
      // problemas técnicos - tente de novo
      break;
    }
    synchronized(this) {
      notify(); // notifica emquanto espera thread
    }
  }

  protected void pauseApp() { }

  protected void destroyApp(boolean arg0) { }

  public void commandAction(Command command, Displayable telaable){
    if (command == sair) {
      destroyApp(true);
      notifyDestroyed();
    }
  }
}

Pequeno exemplo 2

import javax.microedition.payment.*;
...
public class MyGame extends MIDlet implements TransactionListener, CommandListener {
  private TransactionModule myTransactionModule;
  public GamePay2(){
    ...
    try {
      myTransactionModule = new TransactionModule(this);
    }
    catch(TransactionModuleException e) { }
    try {
      myTransactionModule.setListener(this);
    }
    catch(Exception e) { }
  }

  public void startApp(){
    ...
    try {
      myTransactionModule.process(caracteristicasID, ?Titulo de Caracteristicas", "Descrição das  Caracteristicas");
      synchronized(this) {
        try {
          wait(); // espere enquanto a rechamada é realizada
        }
        catch (InterruptedException ie) { }
      }
    }
    catch (Exception e) { }
    ...
  }  

  public void processed(TransactionRecord myPaytRecord){
    switch(myPayRecord.getState()) {
      case TransactionRecord.TRANSACTION_SUCCESSFUL:
        // transação de pagamento bem sucedida
        break;
      case TransactionRecord.TRANSACTION_REJECTED:
        // transação de pagamento rejeitado
        break;
      case TransactionRecord.TRANSACTION_FAILED:
        default:
        // problemas técnicos - tente novamente
        break;
      }
      ...
  }
}

Mais info em: https://sdlc5a.sun.com/ECom/EComActionServlet;jsessionid=EA44F16E59366B5928347F1515619CFB
Site oficial -&gt http://jcp.org/en/jsr/detail?id=229

Arquivo JAD:
Pay-Version: 1.0
Pay-Adapters: PPSMS, X-TEST
MIDlet-Permissions: javax.microedition.payment.process.jpp
MIDlet-Certificate-<n>-<m>: <base64 encoding of a certificate>
MIDlet-Jar-RSA-SHA1: <base64 encoded Jar signature>

Arquivo JAR-Manifest:
Pay-Version: 1.0
Pay-Update-Stamp: 2004-11-15 02:00+01:00
Pay-Providers: SMS1, Test1Card
Pay-Update-URL: http://<update-site>/thisgame.manifest.jpp
Pay-Cache: no
Pay-Feature-0: 0
Pay-Feature-1: 0
Pay-Feature-2: 1
Pay-SMS1-Info: PPSMS, EUR, 928, 99
Pay-SMS1-Tag-0: 1.20, 9990000, 0x0cba98765400
Pay-SMS1-Tag-1: 2.50, 9990000, 0x0cba98765401, 2
Pay-Test1Card-Info: X-TEST8, EUR, c4d21, soap://<soap-site-1>/
Pay-Test1Card-Tag-0: 1.21
Pay-Test1Card-Tag-1: 2.46

Um exemplo complexo do arquivo JAR-Manifest:
Pay-Version: 1.0
Pay-Update-Stamp: 2004-11-15 02:00+01:00
Pay-Providers: SONERA, RADIOG, DNSDNA
Pay-Update-URL: http://<update-site>/thisgame.manifest.jpp
Pay-Cache: no
Pay-Feature-0: 0
Pay-Feature-1: 0
Pay-Feature-2: 1

If user?s operator is ?Sonera?, MNC=928, MCC=99

Pay-SONERA-Info: PPSMS, EUR, 928, 99
Pay-SONERA-Tag-0: 1.20, 9990000, 0x0cba98765400
Pay-SONERA-Tag-1: 2.50, 9990000, 0x0cba98765401, 2

If user?s operator is ?RadioG?, MNC=747, MCC=88

Pay-RADIOG-Info: PPSMS, EUR, 747, 88
Pay-RADIOG-Tag-0: 1.21, 34501, 0xf9b500
Pay-RADIOG-Tag-1: 2.49, 34502, 0xf9b501, 3

If user?s operator is ?DNSDNA?, MNC=380, MCC=77

Pay-DNSDNA-Info: PPSMS, EUR, 380, 77
Pay-DNSDNA-Tag-0: 1.41, 19076501, _DNS
Pay-DNSDNA-Tag-1: 1.99, 19023202, _DNS

Uma alternativa p/ quem precisa realizar um pagamento mas não possui a JSR 229 no aparelho, pode usar conexões HTTPS e post

import java.io.*;
import javax.microedition.midlet.*;
import javax.microedition.io.*;
import javax.microedition.lcdui.*;

public class SslTest extends MIDlet implements CommandListener {
  private final String merchant="4181607";
  private final String amount="19095";
  private final String currency="208";
  private final String orderid ="991002b";
  private final String accepturl ="https://payment.architrade.com/cgissl/relay.cgi/http://www.java4mobile/dibs/godkendt.jsp";
  private final String declineurl ="https://payment.architrade.com/cgissl/relay.cgi/http://www.java4mobile/dibs/afvist.jsp";
  private final String test="foo";

  private String cardno;
  private String expmon;
  private String expyear;
  private String cvc;
  private String url = "https://payment.architrade.com/cgi-ssl/auth.cgi";
  private String post;
  private String urlTotal; 

  private Command exitCommand = new Command("Exit", Command.EXIT, 2);
  private Command getCommand = new Command("Pay", Command.SCREEN, 1);
  private Form form;
  private TextField txtCardno = new TextField("Card no:", null , 16, TextField.NUMERIC);
  private TextField txtExpmon = new TextField("Expmon", null, 2, TextField.NUMERIC);
  private TextField txtExpyear = new TextField("Expyear", null , 2, TextField.NUMERIC);
  private TextField txtCvc = new TextField("Cvc", null, 3, TextField.NUMERIC);
  private Display display; 

  public SslTest() { }

  public void startApp() {
    if (display == null)
      display = Display.getDisplay(this);
    form = new Form("Payment");
    form.append(txtCardno);
    form.append(txtExpmon);
    form.append(txtExpyear);
    form.append(txtCvc);
    form.addCommand(exitCommand);
    form.addCommand(getCommand);
    form.setCommandListener(this);
    display.setCurrent(form);
  }

  public void commandAction(Command c, Displayable d) {
    if (c == exitCommand) {
      notifyDestroyed();
    } 
    else if (c == getCommand) {
      cardno=txtCardno.getString();
      expmon=txtExpmon.getString();
      expyear=txtExpyear.getString();
      cvc=txtCvc.getString();
      post = "?merchant="+merchant+"&amount="+amount+"&currency="+currency+"&orderid="+orderid+ "&accepturl="+accepturl+"&declineurl="+declineurl+"&cardno="+cardno+"&expmon="+expmon+ "&expyear="+expyear+"&cvc="+cvc+"&test="+test;
      StringBuffer b = new StringBuffer();
      HttpsConnection con = null;
      InputStream is = null;
      OutputStream os = null;
      urlTotal = url+post;

      try {
        int len = 0;
        int ch = 0;
        con = (HttpsConnection)Connector.open(urlTotal);
        con.setRequestMethod(HttpsConnection.POST);
        /*
         byte[] data = post.getBytes();
         con.setRequestProperty("Content-Length",
         Integer.toString(data.length));
         os = con.openOutputStream();
         os.write( data );
         os.close();
       */
       System.out.println(Integer.toString(con.getResponseCode()));
       is = con.openInputStream();
       len = (int) con.getLength();
       if (len != -1) {
         for(int i=0; i&lt;len; i++) {
           if((ch = is.read()) != -1) {
             b.append((char) ch);
           }
         }
       }

        else {
          while((ch = is.read()) != -1) {
            len = is.available();
            b.append((char) ch);
          }
        }

       System.out.println("Response: " +b.toString()); 
       Alert a = new Alert("Trans results:", b.toString(), null, null);
       a.setTimeout(Alert.FOREVER);
       display.setCurrent(a);
     }
     catch (Exception e) {
       e.printStackTrace();
       String s = e.toString();
       If(s != null) {
         Alert aa = new Alert("Error in connection:", s, null, null);
         aa.setTimeout(Alert.FOREVER);
         display.setCurrent(aa);
       }
     }

     finally {
     if (is != null) {
       try {
         is.close();
       }
       catch (Exception ce) { }
     }
     if (c != null) {
       try {
         con.close();
       }
       catch (Exception ce) { }
     }
   }
 }
}

 public void pauseApp() {}

 public void destroyApp(boolean unconditional) { }

}

Mais info em: Analysis of J2ME for developing Mobile Payment Systems -> www.microjava.com/articles/techtalk/mpayment

P/ testar se a API é suportada pelo aparelho

import javax.microedition.lcdui.Command;   
import javax.microedition.lcdui.CommandListener;   
import javax.microedition.lcdui.Display;   
import javax.microedition.lcdui.Displayable;   
import javax.microedition.lcdui.Form;   
import javax.microedition.midlet.MIDlet;   
  
public class JSRTest extends MIDlet implements Runnable,CommandListener{   
    Form form;   
    Thread thread;   
    Command c=new Command("Exit",Command.EXIT,0);   
    public JSRTest()   
    {   
        Display.getDisplay(this).setCurrent(form=new Form("JSR Test"));   
        form.addCommand(c);   
        form.setCommandListener(this);   
        (thread=new Thread(this)).start();   
    }   
    protected void destroyApp(boolean u){   
        super.notifyDestroyed();   
    }   
    protected void pauseApp() {   
    }   
    protected void startApp(){   
    }   
    public void run() {   
        checkJSR("MIDP2.0","javax.microedition.lcdui.game.GameCanvas");   
        checkJSR("CLDC1.1","java.lang.Float");   
        checkJSR("MMAPI","javax.microedition.media.Player");   
        checkJSR("WMAPI","javax.wireless.messaging.Message");   
        checkJSR("JSR75","javax.microedition.io.file.FileConnection");   
        checkJSR("JSR082","javax.bluetooth.UUID");   
        checkJSR("JSR179","javax.microedition.location.Location");   
        checkJSR("JSR180","javax.microedition.sip.SipConnection");   
        checkJSR("JSR184","javax.microedition.m3g.Mesh");   
        checkJSR("JSR211","javax.microedition.content.Registry");   
        checkJSR("JSR226","javax.microedition.m2g.SVGImage");   
        checkJSR("JSR229","javax.microedition.payment.TransactionRecord");   
        checkJSR("JSR234","javax.microedition.amms.Module");   
        checkJSR("JSR238","javax.microedition.global.Formatter");   
        checkJSR("JSR239","javax.microedition.khronos.egl.EGL");   
    }   
    private void checkJSR(String jsr,String className)   
    {   
        try {   
            Class.forName(className);   
            form.append(jsr+" Supproted\n");   
        } catch (ClassNotFoundException e) {   
            form.append(jsr+" Not Supproted\n");   
        }   
    }   
    public void commandAction(Command cmd, Displayable disp) {   
        this.destroyApp(false);   
    }   
}

Cade o uso da API Payment ?

Pra mim isto é simplesmente mostra o uso do HTTPS…

A JCP definiu uma API para iniciar transações do pagamento de uma maneira segura.
A API é um agnóstico adaptador de pagamentos, isto é, um suporte ao protocolo real de pagamento (SMS, cartão de crédito,?)

já arrumei