收钱吧-轻POS接口

一次性开发,线上线下全渠道对接所有主流移动支付方式

所有请求格式均采用JSON格式,请求字符集采用UTF-8编码

1、收钱吧官方文档地址:https://doc.shouqianba.com/LitePos/

2、通用请求体样式

{
	"request": {
		"head": {
			"version": "1.0.0",
			"sign_type": "SHA1",
			"appid": "28lp61847655",
			"request_time": "2001-07-04T12:08:56+05:30",
			"reserve": "{}"
		},
		"body": {
			"subject": "this is request business body"
		}
	},
	"signature":"blmSaxUF6/N2XOcz7UWRRVQ5XsVCEz1BpZl6R9Rc6TA3+IfWhJtmCsUZjtw72w1QQ8rEV6+uMh3GWbyzH02Y9dJQCW"
}

3、上干货

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;
import java.security.KeyFactory;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.fastjson.JSONObject;

public class ShouqianbaPush {
	private static Logger logger = LoggerFactory.getLogger(ShouqianbaPush.class);
	/**私钥*/
    private static String privateKeyStr = "收钱吧提供的私钥";
    /**公钥*/
    private static String publicKeyStr = "收钱吧提供的公钥";
    /**轻POS接口地址*/
    private static final String API_DOMAIN = "https://vapi.shouqianba.com/";
    /**APPID*/
    private static final String APP_ID = "你的APPID";
    /**品牌编号,系统对接前由"收钱吧"分配并提供*/
    private static final String BRAND_CODE = "收钱吧提供的品牌编号";
    /**商户内部使用的门店编号*/
    private static final String STORE_SN = "收钱吧提供的门店编号";
    
	
	/**
	 * 收钱吧 推送支付订单信息
	 * @param url 接口地址
	 * @param orderSn 商户订单号,在商户系统中唯一
	 * @param createTime 商户订单创建时间
	 * @param price 订单价格,精确到分
	 * @param subject 订单简短描述,建议传8个字内
	 * @param operator 操作员,可以传入收款的收银员或导购员。例如"张三"
	 * @param customer 可以传入需要备注顾客的信息
	 * @param posInfo 传入商户系统的产品名称、系统编号等信息,便于帮助商户调查问题
	 * @return
	 * @author lan
	 * @since 2020-7-17
	 */
	public static boolean shouqianbaOrderPush(String url,String orderSn,Date createTime,String price,String subject,String operator,String customer,String posInfo) {
		boolean result = false;
		try {
			String timestr = getTime(createTime);
			
			//请求接口参数
			Map<String,Object> param = new HashMap<String,Object>();
            param.put("request_id", UUID.randomUUID());//请求编号,每次请求必须唯一
            param.put("brand_code", BRAND_CODE);//品牌编号,系统对接前由"收钱吧"分配并提供
            param.put("store_sn", STORE_SN);//商户内部使用的门店编号
            param.put("workstation_sn", "0");//门店收银机编号,如果没有请传入"0"
            param.put("check_sn", orderSn);
            param.put("scene", "1");//场景值:1-智能终端,2-H5,4-PC
            param.put("sales_time", timestr);
            param.put("amount", price);
            param.put("currency", "156");//币种
            param.put("subject", subject);
            param.put("operator", operator);
            param.put("customer", customer);
            param.put("industry_code", "0");//行业代码, 0=零售;1:酒店; 2:餐饮; 3:文娱; 4:教育;
            param.put("pos_info", posInfo);
            param.put("notify_url", "接口调用成功后的回调地址");//eg:http://127.0.0.1:8080/projectName/payNotify.json
            
            //传入参数 生成对应签名体
            //注意:完整的请求内容,如步骤1(不包含"request"字段,仅包括request的值)使用RSA私钥签名,并转换为BASE64编码。即:
			/*{
				"head": {
					"version": "1.0.0",
					"sign_type": "SHA1",
					"appid": "你的APPID",
					"request_time": "2001-07-04T12:08:56+05:30"
				},
				"body": {
					"request_id": UUID.randomUUID(),
					"brand_code": BRAND_CODE,
					"store_sn": STORE_SN,
					......
					(请求接口参数全部内容)
				}
			}*/
			String signBody = createSignatureBody(timestr,param);
			//将生成的签名体使用私钥加密,生成签名
			String sign = getSignature(signBody);
			//将参数及签名组装成对应的通用请求体格式
			String paramStr = createParamBody(param,sign,timestr);
			//进行http post请求
			String responseStr = sendPost(API_DOMAIN+url,paramStr);
			Map map = getResponseData(responseStr);
			if(null != map && null != map.get("result_code") && Integer.parseInt(map.get("result_code").toString()) == 200 ){
				Map bizResponse = JSONObject.parseObject(map.get("biz_response").toString(),Map.class);
				if(null != bizResponse && null != bizResponse.get("result_code") && Integer.parseInt(bizResponse.get("result_code").toString()) == 200){
					result = true;
				}
			}
		} catch (Exception e) {
			logger.error("收钱吧 推送支付订单失败",e);
			return false;
		}
		return result;
	}
	
	/**
	 * 获取 YYYY-MM-DDThh:mm:ssTZD 格式时间(时间格式请参考官方文档)
	 * @return
	 * @author lan
	 * @since 2020-6-28
	 */
	private static String getTime(Date date){
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
		if(date == null){
			return sdf.format(new Date());
		}
		return sdf.format(date);
	}
	
	/**
	 * 生成签名体
	 * @param appId 轻POS应用编号,商户入网后由收钱吧技术支持提供
	 * @param storeSn 商户内部使用的门店编号
	 * @param brandCode 品牌编号,系统对接前由"收钱吧"分配并提供
	 * @param checkSn 商户订单号,在商户系统中唯一
	 * @param workstationSn 门店收银机编号,如果没有请传入"0"
	 * @return
	 * @author lan
	 * @since 2020-6-28
	 */
	private static String createSignatureBody(String timestr,Map<String,Object> param){
		String result = null;
		try {
			JSONObject head = new JSONObject();
				head.put("sign_type", "SHA1");
				head.put("appid", APP_ID);
				head.put("request_time", timestr);
				head.put("version", "1.0.0");
			
			JSONObject body = new JSONObject();
				for (String key : param.keySet()) {
					body.put(key, param.get(key));
				}

			JSONObject res = new JSONObject();
				res.put("head", head);
				res.put("body", body);
			
			result = res.toJSONString();
		} catch (Exception e) {
			return null;
		}
		return result;
	}
	
	/**
	 * 获取签名
	 * @param signBody
	 * @return
	 * @author lan
	 * @since 2020-7-17
	 */
	public static String getSignature(String signBody){
		try {
			KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
			gen.initialize(2048);
			PrivateKey privateKey = getPrivateKey(privateKeyStr);
			
			// SHA1withRSA算法进行签名
			Signature sign = Signature.getInstance("SHA1withRSA");
			sign.initSign(privateKey);
			byte[] data = signBody.getBytes();
			// 更新用于签名的数据
			sign.update(data);
			byte[] signature = sign.sign();

			String res = new String(Base64.encodeBase64(signature));
			return res;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 获取私钥
	 * @param privateKey 私钥字符串
	 * @return
	 */
	public static PrivateKey getPrivateKey(String privateKey) throws Exception {
	    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
	    byte[] decodedKey = Base64.decodeBase64(privateKey.getBytes());
	    PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey);
	    return keyFactory.generatePrivate(keySpec);
	}
	
	/**
	 * 获取公钥
	 * @param publicKey 公钥字符串
	 * @return
	 */
	public static PublicKey getPublicKey(String publicKey) throws Exception {
	    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
	    byte[] decodedKey = Base64.decodeBase64(publicKey.getBytes());
	    X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedKey);
	    return keyFactory.generatePublic(keySpec);
	}
	
	/**
	 * 组建参数体
	 * @param appId
	 * @param storeSn
	 * @param brandCode
	 * @param checkSn
	 * @param workstationSn
	 * @return
	 */
	private static String createParamBody(Map<String,Object> param,String signature,String timestr){
		String result = null;
		try {
			JSONObject head = new JSONObject();
				head.put("sign_type", "SHA1");
				head.put("appid", APP_ID);
				head.put("request_time", timestr);
				head.put("version", "1.0.0");
			
			JSONObject body = new JSONObject();
				for (String key : param.keySet()) {
					body.put(key, param.get(key));
				}

			JSONObject request = new JSONObject();
				request.put("head", head);
				request.put("body", body);

			JSONObject res = new JSONObject();
			res.put("request", request);
			res.put("signature", signature);
			
			result = res.toJSONString();
		} catch (Exception e) {
			return null;
		}
		return result;
	}
	
	/**
     * 向指定 URL 发送POST方法的请求
     * @param url 发送请求的 URL
     * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
     * @return 所代表远程资源的响应结果
     */
    public static String sendPost(String url, String param) {
        PrintWriter out = null;
        BufferedReader in = null;
        String result = "";
        try {
            URL realUrl = new URL(url);
            // 打开和URL之间的连接
            URLConnection conn = realUrl.openConnection();
            
            // 设置通用的请求属性
            conn.setRequestProperty("accept", "application/json");
            conn.setRequestProperty("Charset", "UTF-8");
            conn.setRequestProperty("Content-Type","application/json");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
	
            // 发送POST请求必须设置如下两行
            conn.setDoOutput(true);
            conn.setDoInput(true);
            
            // 获取URLConnection对象对应的输出流
            out = new PrintWriter(conn.getOutputStream());
            
            // 发送请求参数
            out.print(param);
            
            // flush输出流的缓冲
            out.flush();
            
            // 定义BufferedReader输入流来读取URL的响应
            in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            System.out.println("发送 POST 请求出现异常!"+e);
            e.printStackTrace();
        }
        //使用finally块来关闭输出流、输入流
        finally{
            try{
                if(out!=null){
                    out.close();
                }
                if(in!=null){
                    in.close();
                }
            }
            catch(IOException ex){
                ex.printStackTrace();
            }
        }
        return result;
    }  
    
    /**
     * 获取返回参数 (返回内容格式请参考官方文档-“响应通用定义”)
     * @param result
     * @return
     * @author lan
     * @since 2020-7-17
     */
	private static Map<String,Object> getResponseData(String result){
		if(result != null){
			JSONObject res = JSONObject.parseObject(result);
			if(res != null && res.get("response") != null){
				String resp = res.get("response").toString();
				JSONObject response = JSONObject.parseObject(resp);
				if(response != null && response.get("body") != null){
					Map map = JSONObject.parseObject(response.get("body").toString(), Map.class);
					return map;
				}else{
					return null;
				}
			}else{
				return null;
			}
		}else{
			return null;
		}
	}

}

4、需要注意下的是签名体的格式问题及时间格式问题,走了些许弯路。另外,所有接口地址全部是小写地址,如果收钱吧技术人员提供的文档上写的是大写地址,请改为小写地址试一试。截止编写此篇文章之时,官方API已更新,处理了以上较为明显的问题。

仅作留存备忘,提供参考之用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值