一.最近要开发支付,考虑到以后接入的支付类型比较多,如常用的,微信,支付宝,银联,以及后期需要接入qq,京东,易宝支付等。为了以后统一管理支付,就考虑使用ping++ 支付,提供了统一的支付接口。
下面我介绍下,利用ping++ 接入支付宝的支付功能。
二.请求服务器 支付接口 orderpay.
1.配置:ping++ api key ,appId,webhooksParse
2.在ping++ 官网 找到 rsa private key ,官网提供的是rsc pacs#1 ,这里需要转成 pkcs8,我是利用在线的一个网站转换 : http://tool.chacuo.net/cryptrsapkcs1pkcs8
3.创建charge对象,这里着重要说明的是 获取客户端ip,这也是官网demo 未强调的点,对于不了解代理请求转发的小伙伴来说,这里又会遇到大坑,项目不上线 不知道,一上线支付就调用不起来,根源就在
x-forwarded-for 获取客户端IP这里。给一个地址介绍下x-forwarded-for :http://www.360doc.com/content/12/0409/15/1073512_202198496.shtml,他最终结果是将所有的代理过的ip地址全拼接在一起,而我 们需要的是最开始发起支付请求的客户端IP。下面是代码部分:
1 public String orderpay(){ 2 //live LIVE_API_BASE = "https://api.pingxx.com"; 3 //Pingpp.overrideApiBase(PingppTestData.getApiBase()); 4 Pingpp.apiKey = Cont.API_KEY_LIVE; 5 // 建议使用 PKCS8 编码的私钥,可以用 openssl 将 PKCS1 转成 PKCS8 6 //Pingpp.privateKey = PingppTestData.getPKCS8PrivateKey(); 7 8 // Pingpp.DEBUG = true; 9 //PKCS8,建议使用 PKCS8 编码的私钥,可以用 openssl 将 PKCS1 转成 PKCS8 10 Pingpp.privateKey = "-----BEGIN PRIVATE KEY-----\n"+ 12 "此处填入pkcs8 私钥\n"+ 13 "-----END PRIVATE KEY-----\n"; 14 15 //私钥 16 //Pingpp.privateKeyPath = ApiCommonAction.class.getClassLoader().getResource("res").getPath()+"/pingpp_private_key.pem";18 String paramObj = getRequest().getParameter("paramObj"); 19 if(!StringUtil.isEmpty(paramObj)){ 20 if(new JsonParser().parse(paramObj).isJsonObject()){ 21 JsonObject jsonobj = new JsonParser().parse(paramObj).getAsJsonObject();23 String amount = jsonobj.get("price").getAsString(); 24 //此处支付时,需将价格 转成 以分为单位 25 //int price = (int) (Double.valueOf(amount) * 100);// 支付时 100 代表 1块。 26 int price = (int) (Double.valueOf(amount) * 100);// 支付时 100 代表 1块。 27 String order_no = jsonobj.get("orderNo").getAsString(); 28 String orderId = jsonobj.get("orderId").getAsString(); 29 String payType = jsonobj.get("paytype").getAsString(); 30 31 String channel = ""; 32 //支付宝 33 if(!StringUtil.isEmpty(payType) && "1".equals(payType)){ 34 channel = "alipay"; 35 36 }else if(!StringUtil.isEmpty(payType) && "2".equals(payType)){//微信 37 channel = "wx"; 38 }else{ 39 setJsonString(AppJSON.errReq("支付类型不能为空!")); 40 return "ajax"; 41 } 42 43 String remoteAddr = getRequest().getRemoteAddr();45 46 String ip = getRequest().getHeader("x-forwarded-for"); 47 if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){ 48 ip = getRequest().getHeader("Proxy-Client-IP"); 49 } 50 if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){ 51 ip = getRequest().getHeader("WL-Proxy-Client-IP"); 52 } 53 if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){ 54 ip = getRequest().getRemoteAddr(); 55 } 56 57 if(!StringUtil.isEmpty(ip)){ 58 String[] iparr = ip.trim().split(","); 59 ip = iparr[0]; 60 } 61 62 System.out.println("客户端++ ip="+ip); 63 64 //String client_ip = "127.0.0.1";//host.substring(0,host.indexOf(":")); 65 String client_ip = ip; 66 67 //subject 和 body 参数用来在用户付款、以及在第三方支付软件的账单显示。从订单表中获取 68 String subject = "预定服务"; 69 String body = "支付订单费用"; 70 71 Charge charge = this.createCharge(orderId,price,order_no,channel,client_ip, subject, body); 72 if(charge!=null){ 73 System.out.println("========ping++ chargeId= "+charge.getId()); 74 // 传到客户端请先转成字符串 .toString(), 调该方法,会自动转成正确的 JSON 字符串 75 76 //以"X"结尾,则标识需求订单, 77 if(order_no.endsWith("X")){ 78 //业务处理82
}else{ 83 //业务处理87
} 88 String chargeString = charge.toString(); 89 90 setJsonString(AppJSON.succReq("请求成功", chargeString)); 91 } 92 } 93 94 }else{ 95 setJsonString(AppJSON.errParas("支付 paramObj不能为空"));return "ajax"; 96 } 97 98 return "ajax"; 99 }
三.创建支付对象 charge
1.指定 支付使用的appid
2.调用 charge对象的create 方法,创建支付对象。
1 public Charge createCharge(String orderId,int price,String orderNo,String channel,String clent_ip,String subject,String body){ 2 //String appId = PingppTestData.getAppID(); 3 String appId = Cont.API_ID; 5 Charge charge = null; 6 Map<String, Object> chargeMap = new HashMap<String, Object>(); 7 chargeMap.put("amount", price);//订单总金额, 人民币单位:分(如订单总金额为 1 元,此处请填 100) 8 chargeMap.put("currency", "cny"); 9 chargeMap.put("subject", subject); 10 chargeMap.put("body", body); 11 chargeMap.put("order_no", orderNo);// 推荐使用 8-20 位,要求数字或字母,不允许其他字符 12 chargeMap.put("channel", channel);// 支付使用的第三方支付渠道取值,请参考:https://www.pingxx.com/api#api-c-new 13 14 Calendar cal = Calendar.getInstance(); 15 cal.add(Calendar.MINUTE, 5);//5分钟失效 16 long timestamp = cal.getTimeInMillis()/ 1000L; 17 chargeMap.put("time_expire", timestamp); 18 Timer timer = new Timer(); 19 //发起一个定时任务,半小时后自动触发,取消订单流程 20 //以"X"结尾,则标识需求订单, 21 if(orderNo.endsWith("X")){ 22 //需求订单 23 timer.schedule(new DemandlobbyNoPayCancel(Long.valueOf(orderId)), 1000 * 60 * 5); 24 }else{ 25 //普通订单 26 timer.schedule(new OrderNoPayCancel(Long.valueOf(orderId)), 1000 * 60 * 5); 27 } 28 29 chargeMap.put("client_ip", clent_ip); // 发起支付请求客户端的 IP 地址,格式为 IPV4,如: 127.0.0.1 30 Map<String, String> app = new HashMap<String, String>(); 31 app.put("id", appId); 32 chargeMap.put("app", app); 33 34 Map<String, Object> extra = new HashMap<String, Object>(); 35 // extra.put("success_url", "http://127.0.0.1/succeeded"); 36 chargeMap.put("extra", extra); 37 try { 38 // 发起 charge 创建请求 39 charge = Charge.create(chargeMap); 40 41 } catch (APIConnectionException e) { 42 e.printStackTrace(); 43 } catch (ChannelException e) { 44 e.printStackTrace(); 45 } catch (RateLimitException e) { 46 e.printStackTrace(); 47 } catch (AuthenticationException e) { 48 e.printStackTrace(); 49 } catch (APIException e) { 50 e.printStackTrace(); 51 } catch (InvalidRequestException e) { 52 e.printStackTrace(); 53 } 54 55 return charge; 56 }
四.支付回调方法
1.回调地址,是在ping++ 官网配置,官网提供 test 和 live 两版本,开发过程先用test 做回调地址配置,测通后,项目正式上线改成live模式.
2.回调地址进行业务处理,回调方法获取event type类型,是支付成功,还是退款成功或者红包发送成功..... 具体可参考 ping++ 开发api: https://www.pingxx.com/api#events-事件
3.贴出我的回调处理方法代码,这里主要会发生的签名验签的错误,那么遇到这种情况,你还是要核对下,是否直接使用了ping++的私钥 并没有转成 pkcs8 加密方式,或者直接拷入了ping++的公钥....
1 public String webhooksParseCharge(){ 2 HttpServletRequest request = getRequest(); 3 HttpServletResponse response = getResponse(); 4 try { 5 request.setCharacterEncoding("UTF8"); 6 } catch (UnsupportedEncodingException e1) { 7 e1.printStackTrace(); 8 } 9 //获取头部所有信息 10 Enumeration<String> headerNames = request.getHeaderNames(); 11 String signature=null; 12 while (headerNames.hasMoreElements()) { 13 String key = (String) headerNames.nextElement(); 14 String value = request.getHeader(key); 15 if("x-pingplusplus-signature".equals(key)){ 16 signature=value; 17 } 18 } 19 20 // 获得 http body 内容 21 StringBuffer eventJson=new StringBuffer(); 22 BufferedReader reader= null; 23 try { 24 reader = request.getReader(); 25 do{ 26 eventJson.append(reader.readLine()); 27 }while(reader.read()!=-1); 28 reader.close(); 29 } catch (IOException e) { 30 e.printStackTrace(); 31 } 32 33 JSONObject event=JSONObject.fromObject(eventJson.toString()); 34 System.out.println("=========event=========== "+event); 35 boolean verifyRS=false; 36 try { 37 PublicKey publicKey = WebhooksVerifyService.getPubKey(); 38 System.out.println("============公钥============"+publicKey); 39 verifyRS = WebhooksVerifyService.verifyData(eventJson.toString(),signature,publicKey); 40 System.out.println("============== 是否签名验证成功! "+ verifyRS); 41 //verifyRS = true; 42 } catch (Exception e) { 43 e.printStackTrace(); 44 } 45 46 if(verifyRS) { 47 // charge.succeeded 支付对象,支付成功时触发。 48 // refund.succeeded 退款对象,退款成功时触发。 49 if ("charge.succeeded".equals(event.get("type"))) { 50 JSONObject data = JSONObject.fromObject(event.get("data").toString()); 51 JSONObject object = JSONObject.fromObject(data.get("object").toString()); 52 53 String channel = (String) object.get("channel"); 54 String payType = null; 55 int amountFen = (int) object.get("amount"); 56 //交易流水号 57 String transaction_no = object.get("transaction_no")+""; 58 59 Double amountYuan = amountFen * 1.0 / 100;//ping++扣款,精确到分,而数据库精确到元 60 Double weiXinInput = null; 61 Double aliPayInput = null; 62 Double bankCardInput = null; 63 64 if ("wx".equals(channel)) { 65 payType = "2";//支付类型(1:支付宝2微信) 66 weiXinInput = amountYuan; 67 } else if ("alipay".equals(channel)) { 68 payType = "1"; 69 aliPayInput = amountYuan; 70 } 71 72 String orderNo = (String) object.get("order_no"); 73 // 支付成功业务处理 74 }else if("refund.succeeded".equals(event.get("type"))){//退款事件 75 // 退款业务处理 76 77 } 78 79 }else{ 80 System.out.println("签名验证失败"); 81 setJsonString(AppJSON.errReq("签名验证失败!")); 82 } 83 84 return "ajax"; 85 }
五. 关于ping++ 支付先介绍到这里,后期有先关支付类技术 我会逐步跟进,如此文大家有什么问题,请留言联系我,很乐意解决大家的问题。
另外郑重说明 本篇文章是个人原创,如需转载请标注原文地址。