支付宝接入参考链接:https://software.intel.com/zh-cn/node/542608
银联接入参考链接:http://blog.csdn.net/gf771115/article/details/41935683
银联支付相关问题记录:
1、我们看文档知道客户端使用银联(Android、IOS)需要有一个获取TN的接口。
2、我们来看银联支付返回回来的逻辑。代码如下:
1 /* 2 * 支付控件返回字符串:success、fail、cancel 分别代表支付成功,支付失败,支付取消 3 */ 4 String str = data.getExtras().getString("pay_result"); 5 if (str.equalsIgnoreCase("success")) { 6 // 支付成功后,extra中如果存在result_data,取出校验 7 // result_data结构见c)result_data参数说明 8 if (data.hasExtra("result_data")) { 9 String result = data.getExtras().getString("result_data"); 10 try { 11 JSONObject resultJson = new JSONObject(result); 12 String sign = resultJson.getString("sign"); 13 String dataOrg = resultJson.getString("data"); 14 // 验签证书同后台验签证书 15 // 此处的verify,商户需送去商户后台做验签 16 boolean ret = RSAUtil.verify(dataOrg, sign, mMode); 17 if (ret) { 18 // 验证通过后,显示支付结果 19 payStatus(true); 20 } else { 21 // 验证不通过后的处理 22 // 建议通过商户后台查询支付结果 23 payStatus(false); 24 } 25 } catch (JSONException e) { 26 e.printStackTrace(); 27 } 28 } else { 29 // 未收到签名信息 30 // 建议通过商户后台查询支付结果 31 payStatus(true); 32 } 33 } else if (str.equalsIgnoreCase("fail")) { 34 payStatus(false); 35 } else if (str.equalsIgnoreCase("cancel")) { 36 payStatus(false); 37 } 38 }
我们看代码的第16行,我们看到在pay_result返回success后,还需要将证书与后台的商户进行验签。这个逻辑应该是最近版本的银联加上的。我看过老版本的没有这一步。测试的时候支付成功后,我们发现验证不通过,所以还是显示支付失败。那么显然是verify方法中的哪一步出现了问题。我们跟下去,看到了下面的代码:
1 public static PublicKey getPublicKeyProduct() { 2 // 请将此处的module换成生产环境商户验签的公钥模数 3 //String modulus = "24882698307025187401768229621661046262584590315978248721358993520593720674589904440569546585666019820242051570504151753011145804842286060932917913063481673780509705461614953345565639235206110825500286080970112119864280897521494849627888301696007067301658192870705725665343356870712277918685009799388229000694331337917299248049043161583425309743997726880393752539043378681782404204317246630750179082094887254614603968643698185220012572776981256942180397391050384441191238689965500817914744059136226832836964600497185974686263216711646940573711995536080829974535604890076661028920284600607547181058581575296480113060083"; 4 String modulus="24882698307025187401768229621661046262584590315978248721358993520593720674589904440569546585666019820242051570504151753011145804842286060932917913063481673780509705461614953345565639235206110825500286080970112119864280897521494849627888301696007067301658192870705725665343356870712277918685009799388229000694331337917299248049043161583425309743997726880393752539043378681782404204317246630750179082094887254614603968643698185220012572776981256942180397391050384441191238689965500817914744059136226832836964600497185974686263216711646940573711995536080829974535604890076661028920284600607547181058581575296480113060083"; 5 String publicExponent = "65537"; 6 PublicKey publicKey = RSAUtil.generateRSAPublicKey(modulus, 7 publicExponent); 8 return publicKey; 9 }
我们看到需要将公钥模数进行替换。起初找了好久没有找到公钥模数。后来发现将生成环境下的证书的信息读取出来就可以获取公钥模数,替换即可。读取证书信息的Java代码如下:
public static void main(String[] args){ try { CertificateFactory certificatefactory=CertificateFactory.getInstance("X.509"); FileInputStream bais=new FileInputStream("d:/aa.cer"); X509Certificate Cert = (X509Certificate)certificatefactory.generateCertificate(bais); RSAPublicKey pk = (RSAPublicKey) Cert.getPublicKey(); sun.security.rsa.RSAPublicKeyImpl ppk = (sun.security.rsa.RSAPublicKeyImpl) pk; System.out.println(ppk.getModulus());//获取公钥模数 System.out.println(ppk.getPublicExponent()); System.out.println(ppk.getAlgorithm()); System.out.println(ppk.getFormat()); System.out.println(ppk.getAlgorithmId()); System.out.println(ppk.getModulus().toString(16)); System.out.println(ppk.getPublicExponent().toString(16));//10001 System.out.println(new BigInteger(ppk.getEncoded()).toString(16)); }catch (Exception e) { e.printStackTrace(); } }