API调用详解 更新时间:2017-09-13 

BMOP的API是基于HTTP协议来调用的,开发者(ISV)可以直接使用BMOP提供的官方SDK(支持JAVA,PHP,.NET语言,包含了请求的封装,签名加密,响应解释,性能优化等)来调用,也可以根据力方开放平台的协议来封装HTTP请求进行调用,以下主要是针对自行封装HTTP请求进行API调用的原理进行详细解说。

一、调用流程

调用介绍

根据力方开放平台API调用协议:封装参数 > 生成签名 > 拼装HTTP请求 > 封装参数 > 发起HTTP请求 > 获取HTTP响应 > 解释json报文,以下是大致的调用示意图:

二、调用入口

调用API的服务的URL地址,目前BMOP提供了2个环境给ISV使用:
1、正式测试环境:ISV应用在开发中(正式上线之前)的正式模拟环境,环境入口和正式环境一样,应用创建后即可进行调用,限制的API调用次数:5000次/天。
2、正式环境:ISV应用在开发测试完毕,需要正式发布上线的环境,上线以后,应用流量包会打开,具体的流量限制根据应用类型和用户规模有关,例如商家后台应用初始:10W次/天,在线订购应用100W次/天.根据用户规模后续可以申请更高的流量包。
调用环境 入口地址(HTTP) 入口地址(HTTPS)
正式测试环境 http://api.bm001.com/api https://api.bm001.com/api
正式环境 http://api.bm001.com/api https://api.bm001.com/api

三、公共参数

API参数由系统参数和应用参数组成,系统级参数是调用任意一个API都必须传入的参数,目前需要传入的系统级参数有:
系统级参数说明
名称 类型 是否必须 描述
method string 必须 API接口方法名称
v string 必须 接口版本号,可选值:1.1
sign string 必须 API输入参数的数字签名,签名算法参照下面的介绍
access_token string 可选 用户登录授权成功以后,力方开放平台会颁发给应用的授权信息access_token(接口访问令牌),API是需要需要传入此参数,请参考各API的用户授权类型。
timestamp number 必须 请求时间戳,时区为GMT+8(北京时间),格式:"yyyy-MM-dd HH:mm:ss",
例如"2012-12-20 10:20:35",力方API服务器允许的时间误差在10分钟。

四、业务参数

业务级参数是每个API本身业务所需要的参数,每个API的业务级参数请参考API文档说明,下面以bm.elife.recharge.mobile.getItemInfo(话费充值-查询单个话费直充商品)。

应用级参数说明
名称 类型 是否必须 描述
mobileNo number 必须 需要充值的手机号码
rechargeAmount number 必须 正整数,例如20、50、100等面值

五、数字签名算法

为了防止API调用过程中被黑客恶意篡改,调用任何一个API都需要携带数字签名,力方开放平台服务端会对签名进行校验,签名不合法的请求将会被拒绝,力方开放平台目前支持的签名算法为SHA1,签名的大致过程如下:

  • 对所有API请求参数(包括公共参数和业务级参数,但除去sign参数本身和image(图片数据)的参数),根据参数名称的ASCII码表的顺序排序。如:bad=2,bac=1,cba=3,排序后的顺序是bac=1,bad=2,cba=3。
  • 将排序好的参数排在一起,根据上面的示例得到的排序后的结果:bac1bad2cba3。
  • 把拼装好的字符串用uft-8编码,需要在拼装后的字符串前后都加上应用的appSecret(应用证书中查看)在进行SHA1摘要,例如应用的appSecret为Banma,进行签名计算:SHA1(Banmabac1bad2cba3Banma)
  • 将摘要得到的字节流转化成16进制,例如将上面的签名摘要转换后的结果为:8AC30853E229E19EB7C8BCA9782D3079CC7399E8

说明:SHA1为安全哈希算法,主要用于数字签名标准(DSS)中的签名算法(DSA),SHA1会产生一个160位的消息摘要,用16进制表示,一个16进制的字符能表示4个位,所以签名后的长度固定为40个16进制字符。

JAVA签名示例代码(其他语言的签名示例请参考力方开放平台官方SDK源码)
package com.qianmi.util;

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.util.*;

/**
 * Created by QianMi
 */
public class SignUtil {
    public static String sha1(String str) throws IOException {
        return byte2hex(getSHA1Digest(str));
    }

    private static byte[] getSHA1Digest(String data) throws IOException {
        byte[] bytes;
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            bytes = md.digest(data.getBytes("utf-8"));
        } catch (GeneralSecurityException gse) {
            throw new IOException(gse);
        }
        return bytes;
    }

    private static String byte2hex(byte[] bytes) {
        StringBuilder sign = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(bytes[i] & 0xFF);
            if (hex.length() == 1) {
                sign.append("0");
            }
            sign.append(hex.toUpperCase());
        }
        return sign.toString();
    }

    public static String sign(Map<String, String> param, String secret) throws IOException {
        if (param == null) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        List<String> paramNames = new ArrayList<>(param.size());
        paramNames.addAll(param.keySet());
        Collections.sort(paramNames);
        sb.append(secret);
        for (String paramName : paramNames) {
            sb.append(paramName).append(param.get(paramName));
        }
        sb.append(secret);
        return sha1(sb.toString());
    }

    public static void main(String[] args) throws IOException {
        Map<String, String> param = new HashMap<>();
        param.put("access_token", "7466bdfc5f79a7fe1defd9a5880a4b84");
        param.put("v", "1.1");
        param.put("method", "bm.elife.recharge.mobile.getItemInfo");
        param.put("timestamp", "1428488009985");
        param.put("mobileNo", "13888888888");
        param.put("rechargeAmount", "100");
        System.out.println(sign(param, "test"));
    }
}

六、调用示例

下面以 bm.elife.recharge.mobile.getItemInfo(话费充值-查询单个话费直充商品)为例说明,具体步骤如下:

1、准备参数项

公共参数:
  • method="bm.elife.recharge.mobile.getItemInfo"
  • v="1.1"
  • access_token="7466bdfc5f79a7fe1defd9a5880a4b84"
  • timestamp="2016-01-01 12:00:00"
业务参数:
  • mobileNo="13888888888"
  • rechargeAmount="100"

2、按ASCII顺序排序

  • access_token="7466bdfc5f79a7fe1defd9a5880a4b84"
  • method="bm.elife.recharge.mobile.getItemInfo"
  • mobileNo="13888888888"
  • rechargeAmount="100"
  • timestamp="2016-01-01 12:00:00"
  • v="1.1"

3、拼接参数名和参数值

连接参数名与参数值,并在首尾加上appSecret,示例如下(假设appSecret值为"test"):
testaccess_token7466bdfc5f79a7fe1defd9a5880a4b84methodbm.elife.recharge.mobile.getItemInfomobileNo13888888888rechargeAmount100timestamp2016-01-01 12:00:00v1.1test

4、生成签名摘要

将上面的拼接好的字符串进行SHA1签名摘要,并转换成16进制字符,最终签名后的sign值为:
CEC5FBC6CEA81E39A9A82BA409DD944F76473059

5、拼装HTTP请求

将所有参数名和参数值采用utf-8进行URL编码(参数顺序可随意,但是必须要包括签名参数sign),然后通过GET或者POST发送HTTP请求,如:
http://api.bm001.com/api?method=bm.elife.recharge.mobile.getItemInfo&v=1.1&access_token=7466bdfc5f79a7fe1defd9a5880a4b84&timestamp=2016-01-01+12%3A00%3A00&mobileNo=13888888888&rechargeAmount=100&sign=CEC5FBC6CEA81E39A9A82BA409DD944F76473059

6、返回结果示例

{
  status: 1,
  message: null,
  data: {
    itemId: "1414504",
    inPrice: "110.000",
    numberChoice: "1-10",
    province: "江苏",
    city: "南京",
    operator: "移动"
  }
}

七、注意事项

  • 所有请求和响应数据编码皆为UTF-8。
  • 没有特别说明的情况下,API接口超时时间为15秒。
  • 部分E生活接口是否成功失败如果返回值有status,则按status值为参考:1成功、0失败。
  • 查询类API建议使用GET请求,增删改类请求请使用POST请求。
  • 生成签名仅对未使用力方官方SDK进行API调用时需要自己生成,如果使用了官方SDK,该步骤会自动完成。
微信号:18851048710