一.最近要開發支付,考慮到以后接入的支付類型比較多,如常用的,微信,支付寶,銀聯,以及后期需要接入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++ 支付先介紹到這里,后期有先關支付類技術 我會逐步跟進,如此文大家有什么問題,請留言聯系我,很樂意解決大家的問題。
另外鄭重說明 本篇文章是個人原創,如需轉載請標注原文地址。