一、请求charge对象
- package com.bra.modules.util.pingplusplus;
- import com.bra.common.utils.SystemPath;
- import com.pingplusplus.Pingpp;
- import org.springframework.stereotype.Service;
- import java.io.File;
- /**
- * Created by Afon on 16/4/26.
- */
- @Service
- public class PingPlusPlusService {
- /**
- * Pingpp 管理平台对应的 API Key
- */
- private final static String apiKey = "";
- /**
- * Pingpp 管理平台对应的应用 ID
- */
- private final static String appId = "";
- /**
- * 你生成的私钥路径
- */
- private final static String privateKeyFilePath = File.separator+SystemPath.getClassPath()+"res"+ File.separator+"rsa_private_key.pem";
- public static String charge(String orderNo,int amount,String subject,String body,String channel,String clientIP){
- // 设置 API Key
- Pingpp.apiKey = apiKey;
- // 设置私钥路径,用于请求签名
- Pingpp.privateKeyPath = privateKeyFilePath;
- PingPlusCharge charge=new PingPlusCharge(appId);
- String chargeString=charge.createCharge(orderNo,amount,subject,body,channel,clientIP);
- return chargeString;
- }
- }
二、生成charge 对象
- package com.bra.modules.util.pingplusplus;
- import com.pingplusplus.exception.PingppException;
- import com.pingplusplus.model.Charge;
- import java.util.Calendar;
- import java.util.HashMap;
- import java.util.Map;
- /**
- * Created by Afon on 16/4/26.
- */
- public class PingPlusCharge {
- private String appId;
- PingPlusCharge(String appId) {
- this.appId = appId;
- }
- public String createCharge(String orderNo, int amount, String subject, String body, String channel, String clientIP) {
- /**
- * 或者直接设置私钥内容
- Pingpp.privateKey = "-----BEGIN RSA PRIVATE KEY-----\n" +
- "... 私钥内容字符串 ...\n" +
- "-----END RSA PRIVATE KEY-----\n";
- */
- Map<String, Object> chargeMap = new HashMap<String, Object>();
- chargeMap.put("amount", amount);
- chargeMap.put("currency", "cny");
- chargeMap.put("subject", subject);
- chargeMap.put("body", body);
- chargeMap.put("order_no", orderNo);
- chargeMap.put("channel", channel);
- Calendar cal = Calendar.getInstance();
- cal.add(Calendar.MINUTE, 15);//15分钟失效
- long timestamp = cal.getTimeInMillis()/ 1000L;
- chargeMap.put("time_expire", timestamp);
- chargeMap.put("client_ip", clientIP); // 客户端 ip 地址(ipv4)
- Map<String, String> app = new HashMap<String, String>();
- app.put("id", appId);
- chargeMap.put("app", app);
- String chargeString = null;
- try {
- //发起交易请求
- Charge charge = Charge.create(chargeMap);
- // 传到客户端请先转成字符串 .toString(), 调该方法,会自动转成正确的 JSON 字符串
- chargeString = charge.toString();
- } catch (PingppException e) {
- e.printStackTrace();
- }
- return chargeString;
- }
- }
三、webhook
- @RequestMapping(value = "webhooks")
- @ResponseBody
- public void webhooks ( HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
- /*System.out.println("ping++ webhooks");*/
- request.setCharacterEncoding("UTF8");
- //获取头部所有信息
- Enumeration headerNames = request.getHeaderNames();
- String signature=null;
- while (headerNames.hasMoreElements()) {
- String key = (String) headerNames.nextElement();
- String value = request.getHeader(key);
- if("x-pingplusplus-signature".equals(key)){
- signature=value;
- }
- }
- /*System.out.println("signature"+signature);*/
- // 获得 http body 内容
- StringBuffer eventJson=new StringBuffer();
- BufferedReader reader= null;
- try {
- reader = request.getReader();
- do{
- eventJson.append(reader.readLine());
- }while(reader.read()!=-1);
- } catch (IOException e) {
- e.printStackTrace();
- }
- reader.close();
- JSONObject event=JSON.parseObject(eventJson.toString());
- boolean verifyRS=false;
- try {
- PublicKey publicKey= WebhooksVerifyService.getPubKey();
- /* System.out.println(publicKey);*/
- verifyRS=WebhooksVerifyService.verifyData(eventJson.toString(),signature,publicKey);
- } catch (Exception e) {
- e.printStackTrace();
- }
- if(verifyRS) {
- /*System.out.println("签名验证成功");*/
- if ("charge.succeeded".equals(event.get("type"))) {
- JSONObject data = JSON.parseObject(event.get("data").toString());
- JSONObject object = JSON.parseObject(data.get("object").toString());
- String orderId = (String) object.get("order_no");
- /*System.out.println("orderId:"+orderId);*/
- String channel = (String) object.get("channel");
- String payType = null;
- int amountFen = (int) object.get("amount");
- Double amountYuan = amountFen * 1.0 / 100;//ping++扣款,精确到分,而数据库精确到元
- Double weiXinInput = null;
- Double aliPayInput = null;
- Double bankCardInput = null;
- if ("wx".equals(channel)) {
- payType = "4";//支付类型(1:储值卡,2:现金,3:银行卡,4:微信,5:支付宝,6:优惠券,7:打白条;8:多方式付款;9:微信个人,10:支付宝(个人))
- weiXinInput = amountYuan;
- } else if ("alipay".equals(channel)) {
- payType = "5";
- aliPayInput = amountYuan;
- } else if ("upacp".equals(channel) || "upacp_wap".equals(channel) || "upacp_pc".equals(channel)) {
- payType = "3";
- bankCardInput = amountYuan;
- }
- Double couponInput;
- ReserveVenueCons order = reserveAppVenueConsService.get(orderId);
- if (order != null) {
- Double orderPrice = order.getShouldPrice();
- couponInput = orderPrice - amountYuan;//订单金额-ping++扣款 等于优惠金额
- Boolean bool = reserveAppVenueConsService.saveSettlement(order, payType, amountYuan,
- 0.0, bankCardInput, weiXinInput, aliPayInput, couponInput);
- if (bool) {
- /* System.out.println("订单结算成功");*/
- response.setStatus(200);
- //return "订单结算成功";
- } else {
- /* System.out.println("订单结算失败");*/
- //return "订单结算失败";
- response.setStatus(500);
- }
- } else {
- /* System.out.println("该订单不存在");*/
- //return "该订单不存在";
- response.setStatus(500);
- }
- }
- }else{
- /*System.out.println("签名验证失败");*/
- //return "签名验证失败";
- response.setStatus(500);
- }
- }
四、WebhooksVerifyService
- package com.bra.modules.util.pingplusplus;
- import com.bra.common.utils.SystemPath;
- import org.apache.commons.codec.binary.Base64;
- import java.io.*;
- import java.security.*;
- import java.security.spec.X509EncodedKeySpec;
- /**
- * Created by sunkai on 15/5/19. webhooks 验证签名示例
- *
- * 该实例演示如何对 Ping++ webhooks 通知进行验证。
- * 验证是为了让开发者确认该通知来自 Ping++ ,防止恶意伪造通知。用户如果有别的验证机制,可以不进行验证签名。
- *
- * 验证签名需要 签名、公钥、验证信息,该实例采用文件存储方式进行演示。
- * 实际项目中,需要用户从异步通知的 HTTP header 中读取签名,从 HTTP body 中读取验证信息。公钥的存储方式也需要用户自行设定。
- *
- * 该实例仅供演示如何验证签名,请务必不要直接 copy 到实际项目中使用。
- *
- */
- public class WebhooksVerifyService {
- private static String pubKeyPath = File.separator+ SystemPath.getClassPath()+"res"+ File.separator+"pingpp_public_key.pem";
- private static String eventPath = File.separator+SystemPath.getClassPath()+"res"+ File.separator+"webhooks_raw_post_data.json";
- private static String signPath = File.separator+SystemPath.getClassPath()+"res"+ File.separator+"signature.txt";
- /**
- * 验证 webhooks 签名,仅供参考
- * @param args
- * @throws Exception
- */
- public static void main(String[] args) throws Exception {
- runDemos();
- }
- public static void runDemos() throws Exception {
- // 该数据请从 request 中获取原始 POST 请求数据, 以下仅作为示例
- String webhooksRawPostData = getStringFromFile(eventPath);
- System.out.println("------- POST 原始数据 -------");
- System.out.println(webhooksRawPostData);
- // 签名数据请从 request 的 header 中获取, key 为 X-Pingplusplus-Signature (请忽略大小写, 建议自己做格式化)
- String signature = getStringFromFile(signPath);
- System.out.println("------- 签名 -------");
- System.out.println(signature);
- boolean result = verifyData(webhooksRawPostData, signature, getPubKey());
- System.out.println("验签结果:" + (result ? "通过" : "失败"));
- }
- /**
- * 读取文件, 部署 web 程序的时候, 签名和验签内容需要从 request 中获得
- * @param filePath
- * @return
- * @throws Exception
- */
- public static String getStringFromFile(String filePath) throws Exception {
- FileInputStream in = new FileInputStream(filePath);
- InputStreamReader inReader = new InputStreamReader(in, "UTF-8");
- BufferedReader bf = new BufferedReader(inReader);
- StringBuilder sb = new StringBuilder();
- String line;
- do {
- line = bf.readLine();
- if (line != null) {
- if (sb.length() != 0) {
- sb.append("\n");
- }
- sb.append(line);
- }
- } while (line != null);
- return sb.toString();
- }
- /**
- * 获得公钥
- * @return
- * @throws Exception
- */
- public static PublicKey getPubKey() throws Exception {
- String pubKeyString = getStringFromFile(pubKeyPath);
- pubKeyString = pubKeyString.replaceAll("(-+BEGIN PUBLIC KEY-+\\r?\\n|-+END PUBLIC KEY-+\\r?\\n?)", "");
- byte[] keyBytes = Base64.decodeBase64(pubKeyString);
- // generate public key
- X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
- KeyFactory keyFactory = KeyFactory.getInstance("RSA");
- PublicKey publicKey = keyFactory.generatePublic(spec);
- return publicKey;
- }
- /**
- * 验证签名
- * @param dataString
- * @param signatureString
- * @param publicKey
- * @return
- * @throws NoSuchAlgorithmException
- * @throws InvalidKeyException
- * @throws SignatureException
- */
- public static boolean verifyData(String dataString, String signatureString, PublicKey publicKey)
- throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, UnsupportedEncodingException {
- byte[] signatureBytes = Base64.decodeBase64(signatureString);
- Signature signature = Signature.getInstance("SHA256withRSA");
- signature.initVerify(publicKey);
- signature.update(dataString.getBytes("UTF-8"));
- return signature.verify(signatureBytes);
- }
- }