Ping++ 支付接口对接

一、请求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);
	}

}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏葬爱家族

RxJava再回首

很早前就看了RxJava,当时就觉得好牛掰,但是公司项目一直没有用起来,知识不用就会忘,前段时间突然要写RxJava,发现已经不会写了。所以今天再回头整理一下R...

23310
来自专栏小樱的经验随笔

凯撒密码加解密及破解实现原理

概念及原理 根据百度百科上的解释,凯撒密码是一种古老的加密算法。 密码的使用最早可以追溯到古罗马时期,《高卢战记》有描述恺撒曾经使用密码来传递信息,即所谓的“恺...

46660
来自专栏刘君君

很方便的密码加密算法BCrypt

2.1K50
来自专栏程序员Gank

【译】对RxJava中-repeatWhen()和-retryWhen()操作符的思考

第一次见到.repeatWhen()和.retryWhen()这两个操作符的时候就非常困惑了。不得不说,它们绝对是“最令人困惑弹珠图”的有力角逐者。

32630
来自专栏武军超python专栏

2018年7月21日python中的加密和解密

·在函数调用执行过程中: 如果出现return,return中的函数执行完则本函数就运行结束,return下面的语句不会再继续执行,所以return使 用时...

31350
来自专栏蘑菇先生的技术笔记

qt5中信号和槽的新语法

37250
来自专栏Java3y

Lucene就是这么简单

什么是Lucene?? Lucene是apache软件基金会发布的一个开放源代码的全文检索引擎工具包,由资深全文检索专家Doug Cutting所撰写,它是一个...

403160
来自专栏数据结构与算法

11:潜伏者

个人感觉这道题在字符串里面算是一个比较综合比较难的题目了 对于选手的数据查找能力有很强的要求 我在做这道题时运用了桶排的思想 不多说了,看代码吧,全网最简 11...

31160
来自专栏码神联盟

Shiro系列 | 《Shiro开发详细教程》第五章:Shiro编码加密

在涉及到密码存储问题上,应该加密或者生成密码摘要存储,而不是存储明文密码。为避免数据泄露对用户造成很大的损失,应该加密或者生成不可逆的摘要方式存储。

15020
来自专栏C#

DotNet加密方式解析--散列加密

   没时间扯淡了,赶紧上车吧。    在现代社会中,信息安全对于每一个人都是至关重要的,例如我们的银行账户安全、支付宝和微信账户安全、以及邮箱等等,说到信息安...

24780

扫码关注云+社区

领取腾讯云代金券